import sys import os import subprocess from app import create_app, db from app.models import Folder, User, ProcessedEmail from app.imap_service import IMAPService from app.processed_emails_service import ProcessedEmailsService from flask.cli import with_appcontext import click app = create_app() @app.cli.command() @with_appcontext def shell(): """Run a shell in the app context.""" import code code.interact(local=dict(globals(), **locals())) @app.cli.command("setup-dev") def setup_dev(): """Set up development environment with Docker Compose.""" # Create tmp directory for IMAP data if it doesn't exist os.makedirs('tmp/imap-data', exist_ok=True) # Start the services try: subprocess.run(['docker-compose', 'up', '-d'], check=True) print("Services started successfully:") print("- PostgreSQL: localhost:5432 (database: email_organizer_dev, user: postgres, password: password)") print("- IMAP Server: localhost:1143") print(" Users:") print(" - user1@example.com / password1") print(" - user2@example.com / password2") print(" Folders: INBOX, Pending, Work, Personal, Receipts, Marketing, Archived") except subprocess.CalledProcessError as e: print(f"Error starting services: {e}") sys.exit(1) except FileNotFoundError: print("Docker Compose not found. Please install Docker and Docker Compose.") sys.exit(1) def mock_process_emails(): """Simulate processing emails with defined rules.""" with app.app_context(): # Mock user ID mock_user_id = '123e4567-e89b-12d3-a456-426614174000' folders = Folder.query.filter_by(user_id=mock_user_id).all() # Mock emails emails = [ {'subject': 'Your Amazon Order', 'from': 'no-reply@amazon.com', 'body': 'Your order has shipped.'}, {'subject': 'Meeting Reminder', 'from': 'boss@company.com', 'body': 'Don\'t forget the meeting at 3 PM.'}, {'subject': 'Special Offer!', 'from': 'deals@shop.com', 'body': 'Exclusive discounts inside!'} ] print("Starting mock email processing...") for email in emails: print(f"\nProcessing email: {email['subject']}") matched = False for folder in folders: # Simple mock rule matching (in real app, this would be more complex) if folder.rule_text.lower() in email['subject'].lower() or folder.rule_text.lower() in email['from'].lower(): print(f" -> Matched rule '{folder.rule_text}' -> Folder '{folder.name}'") matched = True break if not matched: print(" -> No matching rule found.") @app.cli.command("process-pending-emails") @click.option("--user-id", help="Process emails for a specific user ID only") @click.option("--folder-name", help="Process emails for a specific folder only") @click.option("--batch-size", default=100, help="Number of emails to process in each batch") @click.option("--dry-run", is_flag=True, help="Show what would be processed without actually processing") @with_appcontext def process_pending_emails(user_id, folder_name, batch_size, dry_run): """Process pending emails for all users or specific user/folder.""" from app.processed_emails_service import ProcessedEmailsService with app.app_context(): # Build query for users user_query = User.query if user_id: user_query = user_query.filter_by(id=int(user_id)) users = user_query.all() if not users: print("No users found to process") return total_processed = 0 total_users = len(users) print(f"Starting email processing for {total_users} user(s)...") for i, user in enumerate(users, 1): print(f"\nProcessing user {i}/{total_users}: {user.email}") # Skip users without IMAP configuration if not user.imap_config: print(" - Skipping: No IMAP configuration") continue try: # Initialize services imap_service = IMAPService(user) processed_emails_service = ProcessedEmailsService(user) # Get folders for this user folders = Folder.query.filter_by(user_id=user.id).all() if not folders: print(" - No folders found for this user") continue # Process each folder for folder in folders: # Skip if specific folder name is specified and doesn't match if folder_name and folder.name != folder_name: continue print(f" - Processing folder: {folder.name}") # Get all pending emails for this folder pending_emails = ProcessedEmail.query.filter_by( user_id=user.id, folder_name=folder.name, is_processed=False ).all() if not pending_emails: print(f" - No pending emails in this folder") continue total_pending = len(pending_emails) print(f" - Found {total_pending} pending emails") # Process in batches processed_in_folder = 0 for j in range(0, total_pending, batch_size): batch = pending_emails[j:j + batch_size] batch_uids = [email.email_uid for email in batch] if dry_run: print(f" - Dry run: Would process {len(batch_uids)} emails") processed_in_folder += len(batch_uids) else: # Mark emails as processed result = processed_emails_service.mark_emails_processed( folder_name=folder.name, email_uids=batch_uids ) processed_in_folder += result print(f" - Processed {result} emails in this batch") # Commit after each batch to avoid large transactions db.session.commit() print(f" - Total processed in folder: {processed_in_folder}/{total_pending}") total_processed += processed_in_folder except Exception as e: print(f" - Error processing user {user.email}: {str(e)}") import traceback traceback.print_exc() db.session.rollback() continue if not dry_run: print(f"\nCompleted! Total emails processed: {total_processed}") else: print(f"\nDry run completed! Total emails would be processed: {total_processed}") if __name__ == '__main__': if len(sys.argv) > 1 and sys.argv[1] == 'mock-process': mock_process_emails() else: print("Usage: python manage.py mock-process") print(" flask setup-dev")