Spaces:
Running
Running
| """ | |
| Demo seed script β creates a polished demo account (alex@bankbot.dev) | |
| with realistic financial data: transactions, goals, investments, | |
| subscriptions, notifications, and a fraud alert. | |
| Run: python app/scripts/seed_demo.py | |
| """ | |
| import os | |
| import sys | |
| import uuid | |
| import random | |
| from datetime import datetime, timedelta | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) | |
| import bcrypt as _bcrypt | |
| from app.database.database import SessionLocal, engine | |
| from app.database.models import ( | |
| Base, User, Account, Transaction, Subscription, | |
| Goal, Investment, Notification, FraudLog | |
| ) | |
| Base.metadata.create_all(bind=engine) | |
| DEMO_EMAIL = "alex@bankbot.dev" | |
| DEMO_PASSWORD = "BankBot2026!" | |
| DEMO_NAME = "Alex Doe" | |
| def hash_pw(pw: str) -> str: | |
| return _bcrypt.hashpw(pw.encode(), _bcrypt.gensalt(rounds=12)).decode() | |
| def uid() -> str: | |
| return str(uuid.uuid4()) | |
| def seed(): | |
| db = SessionLocal() | |
| try: | |
| # ββ Remove existing demo user ββββββββββββββββββββββββββββββββββββββββ | |
| existing = db.query(User).filter(User.email == DEMO_EMAIL).first() | |
| if existing: | |
| db.delete(existing) | |
| db.commit() | |
| print(f"Removed existing demo user: {DEMO_EMAIL}") | |
| # ββ Create demo user βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| user = User( | |
| id=uid(), | |
| email=DEMO_EMAIL, | |
| password_hash=hash_pw(DEMO_PASSWORD), | |
| profile_data={ | |
| "name": DEMO_NAME, | |
| "phone": "+1 (555) 012-3456", | |
| "avatar": "AD", | |
| "member_since": "2023-01-15", | |
| "plan": "Premium", | |
| }, | |
| financial_personality="Balanced Investor", | |
| ai_personalization_settings={ | |
| "risk_tolerance": "moderate", | |
| "investment_horizon": "long_term", | |
| "notifications": "all", | |
| "ai_tone": "analytical", | |
| }, | |
| ) | |
| db.add(user) | |
| db.flush() | |
| print(f"Created demo user: {DEMO_EMAIL}") | |
| # ββ Accounts βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| checking = Account(id=uid(), user_id=user.id, type="checking", | |
| balance=12_847.32, currency="USD", status="active") | |
| savings = Account(id=uid(), user_id=user.id, type="savings", | |
| balance=28_450.00, currency="USD", status="active") | |
| invest = Account(id=uid(), user_id=user.id, type="investment", | |
| balance=18_340.50, currency="USD", status="active") | |
| db.add_all([checking, savings, invest]) | |
| db.flush() | |
| print("Created 3 accounts (checking $12,847 | savings $28,450 | investment $18,340)") | |
| # ββ Transactions β 6 months of realistic data βββββββββββββββββββββββββ | |
| now = datetime.utcnow() | |
| merchants = [ | |
| # (name, category, type, amount_range) | |
| ("Salary Deposit", "Income", "credit", (4800, 5200)), | |
| ("Freelance Payment", "Income", "credit", (800, 2000)), | |
| ("Whole Foods", "Groceries", "debit", (45, 180)), | |
| ("Trader Joe's", "Groceries", "debit", (30, 120)), | |
| ("Netflix", "Entertainment", "debit", (15, 16)), | |
| ("Spotify", "Entertainment", "debit", (9, 10)), | |
| ("Amazon", "Shopping", "debit", (25, 250)), | |
| ("Apple Store", "Tech", "debit", (10, 200)), | |
| ("Uber", "Transport", "debit", (8, 45)), | |
| ("Shell Gas", "Transport", "debit", (40, 80)), | |
| ("Starbucks", "Food", "debit", (5, 18)), | |
| ("Chipotle", "Food", "debit", (10, 25)), | |
| ("Planet Fitness", "Health", "debit", (24, 25)), | |
| ("CVS Pharmacy", "Health", "debit", (12, 60)), | |
| ("Con Edison", "Utilities", "debit", (80, 140)), | |
| ("Verizon", "Utilities", "debit", (85, 90)), | |
| ("Rent Payment", "Housing", "debit", (1950, 1950)), | |
| ("Dividend Income", "Investment", "credit", (120, 350)), | |
| ("Restaurant", "Food", "debit", (30, 90)), | |
| ("Target", "Shopping", "debit", (40, 150)), | |
| ] | |
| txns = [] | |
| for month_offset in range(6): | |
| month_start = (now.replace(day=1) - timedelta(days=month_offset * 30)) | |
| # Salary on 1st | |
| txns.append(Transaction( | |
| id=uid(), account_id=checking.id, | |
| amount=random.uniform(4800, 5200), type="credit", | |
| category="Income", merchant="Salary Deposit", | |
| timestamp=month_start + timedelta(hours=9), | |
| tags=["recurring", "income"], | |
| )) | |
| # Rent on 3rd | |
| txns.append(Transaction( | |
| id=uid(), account_id=checking.id, | |
| amount=1950.00, type="debit", | |
| category="Housing", merchant="Rent Payment", | |
| timestamp=month_start + timedelta(days=2, hours=10), | |
| tags=["recurring", "housing"], | |
| )) | |
| # Random daily transactions | |
| for _ in range(random.randint(18, 28)): | |
| m = random.choice(merchants[2:]) # skip salary/rent | |
| days_offset = random.randint(0, 28) | |
| hours_offset = random.randint(7, 22) | |
| txns.append(Transaction( | |
| id=uid(), account_id=checking.id, | |
| amount=round(random.uniform(*m[3]), 2), | |
| type=m[2], category=m[1], merchant=m[0], | |
| timestamp=month_start + timedelta(days=days_offset, hours=hours_offset), | |
| tags=[m[1].lower()], | |
| )) | |
| # One suspicious transaction for fraud demo | |
| fraud_txn = Transaction( | |
| id=uid(), account_id=checking.id, | |
| amount=847.00, type="debit", | |
| category="Shopping", merchant="Tech Store NYC", | |
| timestamp=now - timedelta(hours=2), | |
| tags=["flagged"], | |
| spending_emotion_label="impulsive", | |
| ) | |
| txns.append(fraud_txn) | |
| db.add_all(txns) | |
| db.flush() | |
| print(f"Created {len(txns)} transactions across 6 months") | |
| # ββ Fraud log for the suspicious transaction ββββββββββββββββββββββββββ | |
| fraud_log = FraudLog( | |
| id=uid(), | |
| transaction_id=fraud_txn.id, | |
| risk_score=0.78, | |
| suspicious_activity_details=( | |
| "Transaction amount ($847.00) is 3.2x above historical average. " | |
| "Location anomaly: merchant in NYC, usual activity in Brooklyn. " | |
| "Placed at 11:47 PM β outside normal spending hours." | |
| ), | |
| status="pending", | |
| ) | |
| db.add(fraud_log) | |
| print("Created fraud alert for demo transaction") | |
| # ββ Goals βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| goals = [ | |
| Goal(id=uid(), user_id=user.id, title="Emergency Fund", | |
| target_amount=18_000, current_amount=14_200, | |
| target_date=now + timedelta(days=90), | |
| ai_generated_plan={"monthly_contribution": 1267, "months_remaining": 3}), | |
| Goal(id=uid(), user_id=user.id, title="Europe Vacation", | |
| target_amount=5_000, current_amount=2_800, | |
| target_date=now + timedelta(days=180), | |
| ai_generated_plan={"monthly_contribution": 367, "months_remaining": 6}), | |
| Goal(id=uid(), user_id=user.id, title="MacBook Pro", | |
| target_amount=2_500, current_amount=1_900, | |
| target_date=now + timedelta(days=45), | |
| ai_generated_plan={"monthly_contribution": 300, "months_remaining": 2}), | |
| Goal(id=uid(), user_id=user.id, title="Down Payment Fund", | |
| target_amount=80_000, current_amount=28_450, | |
| target_date=now + timedelta(days=730), | |
| ai_generated_plan={"monthly_contribution": 2148, "months_remaining": 24}), | |
| ] | |
| db.add_all(goals) | |
| print(f"Created {len(goals)} financial goals") | |
| # ββ Investments βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| investments = [ | |
| Investment(id=uid(), user_id=user.id, asset_name="S&P 500 Index Fund", | |
| type="mutual_fund", amount_invested=8_000, current_value=9_840, | |
| portfolio_allocation=53.6, | |
| ai_risk_analysis={"risk": "moderate", "expected_return": "8-10%", "recommendation": "hold"}), | |
| Investment(id=uid(), user_id=user.id, asset_name="Apple Inc (AAPL)", | |
| type="stock", amount_invested=3_000, current_value=3_720, | |
| portfolio_allocation=20.3, | |
| ai_risk_analysis={"risk": "moderate-high", "expected_return": "12-15%", "recommendation": "hold"}), | |
| Investment(id=uid(), user_id=user.id, asset_name="Bitcoin (BTC)", | |
| type="crypto", amount_invested=2_500, current_value=2_980, | |
| portfolio_allocation=16.2, | |
| ai_risk_analysis={"risk": "high", "expected_return": "variable", "recommendation": "reduce_exposure"}), | |
| Investment(id=uid(), user_id=user.id, asset_name="US Treasury Bonds", | |
| type="bond", amount_invested=1_800, current_value=1_800, | |
| portfolio_allocation=9.8, | |
| ai_risk_analysis={"risk": "low", "expected_return": "4.5%", "recommendation": "hold"}), | |
| ] | |
| db.add_all(investments) | |
| print(f"Created {len(investments)} investments (total value: ${sum(i.current_value for i in investments):,.0f})") | |
| # ββ Subscriptions βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| subscriptions = [ | |
| Subscription(id=uid(), user_id=user.id, merchant="Netflix", | |
| amount=15.99, billing_cycle="monthly", active=True, | |
| ai_usage_detection={"last_used": "2 days ago", "usage_frequency": "high"}), | |
| Subscription(id=uid(), user_id=user.id, merchant="Spotify", | |
| amount=9.99, billing_cycle="monthly", active=True, | |
| ai_usage_detection={"last_used": "today", "usage_frequency": "daily"}), | |
| Subscription(id=uid(), user_id=user.id, merchant="Adobe Creative Cloud", | |
| amount=54.99, billing_cycle="monthly", active=True, | |
| ai_usage_detection={"last_used": "45 days ago", "usage_frequency": "low"}), | |
| Subscription(id=uid(), user_id=user.id, merchant="Planet Fitness", | |
| amount=24.99, billing_cycle="monthly", active=True, | |
| ai_usage_detection={"last_used": "1 week ago", "usage_frequency": "medium"}), | |
| Subscription(id=uid(), user_id=user.id, merchant="iCloud Storage", | |
| amount=2.99, billing_cycle="monthly", active=True, | |
| ai_usage_detection={"last_used": "today", "usage_frequency": "daily"}), | |
| Subscription(id=uid(), user_id=user.id, merchant="LinkedIn Premium", | |
| amount=39.99, billing_cycle="monthly", active=True, | |
| ai_usage_detection={"last_used": "60 days ago", "usage_frequency": "very_low"}), | |
| ] | |
| db.add_all(subscriptions) | |
| monthly_sub_cost = sum(s.amount for s in subscriptions) | |
| print(f"Created {len(subscriptions)} subscriptions (${monthly_sub_cost:.2f}/month)") | |
| # ββ Notifications βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| notifications = [ | |
| Notification(id=uid(), user_id=user.id, | |
| title="π¨ Unusual Transaction Detected", | |
| message="A charge of $847.00 at 'Tech Store NYC' was flagged. " | |
| "This is 3.2x your average transaction and occurred at 11:47 PM. " | |
| "Please review and confirm.", | |
| type="alert", read_status=False, | |
| created_at=now - timedelta(hours=2)), | |
| Notification(id=uid(), user_id=user.id, | |
| title="π‘ AI Weekly Insight", | |
| message="Your savings rate this month is 38.4% β 18% above the national average. " | |
| "At this pace, you'll reach your Emergency Fund goal in 3 months.", | |
| type="insight", read_status=False, | |
| created_at=now - timedelta(hours=6)), | |
| Notification(id=uid(), user_id=user.id, | |
| title="β οΈ Budget Alert: Shopping", | |
| message="You've spent $847 in Shopping this month β 141% of your $600 budget. " | |
| "Consider pausing non-essential purchases for the rest of the month.", | |
| type="warning", read_status=False, | |
| created_at=now - timedelta(hours=8)), | |
| Notification(id=uid(), user_id=user.id, | |
| title="π― Goal Milestone Reached", | |
| message="Your Emergency Fund is now 78.9% funded ($14,200 of $18,000). " | |
| "You're on track to complete it by August 2026.", | |
| type="insight", read_status=True, | |
| created_at=now - timedelta(days=1)), | |
| Notification(id=uid(), user_id=user.id, | |
| title="π Monthly Report Ready", | |
| message="Your May 2026 financial report is ready. " | |
| "Net savings: $1,847. Top category: Housing (38%). " | |
| "Health score improved by 3 points to 82/100.", | |
| type="insight", read_status=True, | |
| created_at=now - timedelta(days=2)), | |
| Notification(id=uid(), user_id=user.id, | |
| title="π° Subscription Optimization", | |
| message="AI detected 2 underused subscriptions: Adobe CC ($54.99/mo, last used 45 days ago) " | |
| "and LinkedIn Premium ($39.99/mo, last used 60 days ago). " | |
| "Cancelling both saves $1,139.76/year.", | |
| type="warning", read_status=True, | |
| created_at=now - timedelta(days=3)), | |
| ] | |
| db.add_all(notifications) | |
| print(f"Created {len(notifications)} notifications ({sum(1 for n in notifications if not n.read_status)} unread)") | |
| db.commit() | |
| print("\n" + "="*60) | |
| print("DEMO ACCOUNT SEEDED SUCCESSFULLY") | |
| print("="*60) | |
| print(f" Email: {DEMO_EMAIL}") | |
| print(f" Password: {DEMO_PASSWORD}") | |
| print(f" Balance: ${checking.balance + savings.balance + invest.balance:,.2f} total") | |
| print(f" Score: 82/100 (estimated)") | |
| print(f" Fraud: 1 pending alert") | |
| print("="*60) | |
| except Exception as e: | |
| db.rollback() | |
| print(f"SEED FAILED: {e}") | |
| raise | |
| finally: | |
| db.close() | |
| if __name__ == "__main__": | |
| seed() | |