""" Email Preferences API - User email report scheduling settings Stores and retrieves user preferences for daily/weekly email reports """ from fastapi import APIRouter, HTTPException, Header, Depends from pydantic import BaseModel from typing import Optional import json from pathlib import Path import logging import os import httpx from datetime import datetime from services.email_service import send_insight_email import re from database.db import get_db logger = logging.getLogger(__name__) router = APIRouter() # Storage directory for user preferences # email_prefs.py is at: backend/api/v1/endpoints/email_prefs.py # We need to get to: backend/storage/email_prefs # So we need 4 parent levels: endpoints -> v1 -> api -> backend import os # Use absolute path that matches scheduler PREFS_DIR = Path(__file__).parent.parent.parent.parent / "storage" / "email_prefs" PREFS_DIR.mkdir(parents=True, exist_ok=True) logger.info(f"Email Prefs PREFS_DIR: {PREFS_DIR.absolute()}") class EmailPreferences(BaseModel): """User email report preferences""" daily_report_enabled: bool = False daily_report_hour: int = 8 # 8 AM default daily_report_minute: int = 0 # 0 minutes default weekly_report_enabled: bool = True weekly_report_day: int = 1 # Monday (0=Sun, 1=Mon, ..., 6=Sat) weekly_report_hour: int = 9 # 9 AM default weekly_report_minute: int = 0 # 0 minutes default email_address: Optional[str] = None # Override profile email if needed timezone_offset: float = 5.5 # Default IST (UTC+5:30), can be changed by user def get_prefs_path(user_id: str) -> Path: """Get path to user's preferences file""" return PREFS_DIR / f"{user_id}_email_prefs.json" def load_user_prefs(user_id: str) -> EmailPreferences: """Load user preferences from file""" prefs_path = get_prefs_path(user_id) if prefs_path.exists(): try: with open(prefs_path, 'r') as f: data = json.load(f) return EmailPreferences(**data) except Exception as e: logger.error(f"Error loading prefs for {user_id}: {e}") # Return defaults return EmailPreferences() def save_user_prefs(user_id: str, prefs: EmailPreferences) -> None: """Save user preferences to file""" prefs_path = get_prefs_path(user_id) try: print(f"💾 SAVING email prefs for user '{user_id}' to: {prefs_path.absolute()}") with open(prefs_path, 'w') as f: json.dump(prefs.dict(), f, indent=2) print(f"✅ SAVED successfully! File exists: {prefs_path.exists()}") logger.info(f"Saved email prefs for user {user_id}") except Exception as e: print(f"❌ FAILED to save: {e}") logger.error(f"Error saving prefs for {user_id}: {e}") raise @router.get("/email-prefs") async def get_email_preferences( x_user_id: str = Header(None, alias="X-User-ID") ): """Get user's email report preferences""" user_id = x_user_id or "default_user" prefs = load_user_prefs(user_id) return { "success": True, "preferences": prefs.dict(), "day_options": [ {"value": 0, "label": "Sunday"}, {"value": 1, "label": "Monday"}, {"value": 2, "label": "Tuesday"}, {"value": 3, "label": "Wednesday"}, {"value": 4, "label": "Thursday"}, {"value": 5, "label": "Friday"}, {"value": 6, "label": "Saturday"}, ], "hour_options": [ {"value": h, "label": f"{h:02d}:00"} for h in range(24) ] } @router.put("/email-prefs") async def update_email_preferences( prefs: EmailPreferences, x_user_id: str = Header(None, alias="X-User-ID") ): """Update user's email report preferences""" user_id = x_user_id or "default_user" # Validate hour (0-23) if not (0 <= prefs.daily_report_hour <= 23): raise HTTPException(400, "daily_report_hour must be 0-23") if not (0 <= prefs.weekly_report_hour <= 23): raise HTTPException(400, "weekly_report_hour must be 0-23") # Validate day (0-6) if not (0 <= prefs.weekly_report_day <= 6): raise HTTPException(400, "weekly_report_day must be 0-6 (Sun-Sat)") try: save_user_prefs(user_id, prefs) return { "success": True, "message": "Email preferences updated successfully", "preferences": prefs.dict() } except Exception as e: raise HTTPException(500, f"Failed to save preferences: {str(e)}") class TestEmailRequest(BaseModel): """Request body for test email""" email_address: str @router.post("/email-prefs/test") async def test_email_report( request: TestEmailRequest = None, x_user_id: str = Header(None, alias="X-User-ID") ): """Send a test email report to verify configuration""" from services.email_service import send_insight_email user_id = x_user_id or "default_user" # Get email from request body OR from saved preferences email_to_use = None if request and request.email_address: email_to_use = request.email_address else: prefs = load_user_prefs(user_id) email_to_use = prefs.email_address if not email_to_use: raise HTTPException(400, "No email address provided. Please enter your email address.") try: print(f"📧 Sending test email to: {email_to_use}") await send_insight_email( to_email=email_to_use, title="DataVision - Test Email | Configuration Verified", body="This is a test email from DataVision. Your email configuration is working correctly. You will receive automated data insights based on your preferences.", workspace_id=user_id ) return { "success": True, "message": f"Test email sent to {email_to_use}" } except Exception as e: print(f"❌ Email send failed: {e}") raise HTTPException(500, f"Failed to send test email: {str(e)}") def generate_fallback_email_body(data_context: str) -> str: """Generate a nicely formatted email body when AI is unavailable""" from datetime import datetime return f"""

EXECUTIVE SUMMARY - {datetime.now().strftime('%B %d, %Y')}

DataVision Autonomous Agents have compiled the latest intelligence regarding your connected datasets.

📊 Data Intelligence Overview

{data_context.replace(chr(10), '
')}

🚀 Recommended Actions

""" @router.post("/email-prefs/send-daily-report") async def send_daily_report_now( request: TestEmailRequest = None, x_user_id: str = Header(None, alias="X-User-ID") ): """ 🤖 AI-POWERED DAILY REPORT Automatically generates personalized email content using: - Real uploaded data insights - ML training results (if available) - Dashboard analytics """ import os import httpx from services.email_service import send_insight_email from datetime import datetime user_id = x_user_id or "default_user" # Get email from request body OR from saved preferences email_to_use = None if request and request.email_address: email_to_use = request.email_address else: prefs = load_user_prefs(user_id) email_to_use = prefs.email_address if not email_to_use: raise HTTPException(400, "No email address provided. Please enter your email address.") try: # 1. Gather REAL user data context print(f"🔍 Gathering real data context for user: {user_id}") data_context = await gather_user_data_context(user_id) print(f"📊 Data context:\n{data_context}") if data_context == "No data uploaded yet.": raise HTTPException(400, "No data available. Please upload some data first to generate a report.") # 2. Use AI to generate personalized email content WITH explanations groq_api_key = os.getenv("GROQ_API_KEY") if groq_api_key: print(f"🤖 Using AI to generate personalized report with ML explanations...") system_prompt = """You are DataVision AI, an elite enterprise business intelligence expert. Generate a highly professional, executive-level email report. Your email should: 1. Have a clear, engaging SUBJECT LINE (start with "Subject: ") 2. Be formatted in clean HTML suitable for an enterprise report (use

,

,

,