Initial commit: WealthWise financial analytics platform
This commit is contained in:
25
backend/scripts/init_db.py
Normal file
25
backend/scripts/init_db.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""Initialize database tables.
|
||||
|
||||
This script creates all database tables defined in the models.
|
||||
Run this before starting the application for the first time.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from app.core.config import get_settings
|
||||
from app.models.base import BaseModel
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
async def init_db():
|
||||
"""Create all database tables."""
|
||||
engine = create_async_engine(settings.DATABASE_URL, echo=True)
|
||||
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(BaseModel.metadata.create_all)
|
||||
|
||||
await engine.dispose()
|
||||
print("Database tables created successfully!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(init_db())
|
||||
57
backend/scripts/init_migrations.sh
Normal file
57
backend/scripts/init_migrations.sh
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
|
||||
# WealthWise Database Migration Script
|
||||
# This script initializes Alembic and creates the initial migration
|
||||
|
||||
echo "Setting up Alembic migrations for WealthWise..."
|
||||
|
||||
# Check if alembic is installed
|
||||
if ! command -v alembic &> /dev/null; then
|
||||
echo "Installing alembic..."
|
||||
pip install alembic
|
||||
fi
|
||||
|
||||
# Initialize Alembic if not already initialized
|
||||
if [ ! -d "alembic" ]; then
|
||||
echo "Initializing Alembic..."
|
||||
alembic init alembic
|
||||
|
||||
# Update alembic.ini with database URL
|
||||
sed -i '' 's|sqlalchemy.url = driver://user:pass@localhost/dbname|sqlalchemy.url = postgresql://postgres:postgres@localhost:5432/wealthwise|' alembic/alembic.ini 2>/dev/null || sed -i 's|sqlalchemy.url = driver://user:pass@localhost/dbname|sqlalchemy.url = postgresql://postgres:postgres@localhost:5432/wealthwise|' alembic/alembic.ini
|
||||
|
||||
echo "Alembic initialized successfully!"
|
||||
else
|
||||
echo "Alembic already initialized."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Update alembic/env.py to import your models (see instructions below)"
|
||||
echo "2. Run: alembic revision --autogenerate -m 'Initial migration'"
|
||||
echo "3. Run: alembic upgrade head"
|
||||
echo ""
|
||||
echo "=== alembic/env.py Configuration ==="
|
||||
echo "Add these imports to alembic/env.py:"
|
||||
echo ""
|
||||
echo "import asyncio"
|
||||
echo "from sqlalchemy.ext.asyncio import AsyncEngine"
|
||||
echo "from app.models import User, Portfolio, Transaction"
|
||||
echo "from app.models.base import BaseModel"
|
||||
echo ""
|
||||
echo "Then update run_migrations_online() to use async engine:"
|
||||
echo ""
|
||||
echo "def do_run_migrations(connection):"
|
||||
echo " context.configure(connection=connection, target_metadata=BaseModel.metadata)"
|
||||
echo " with context.begin_transaction():"
|
||||
echo " context.run_migrations()"
|
||||
echo ""
|
||||
echo "async def run_migrations_online():"
|
||||
echo " connectable = AsyncEngine(create_async_engine(config.get_main_option('sqlalchemy.url')))"
|
||||
echo " async with connectable.connect() as connection:"
|
||||
echo " await connection.run_sync(do_run_migrations)"
|
||||
echo " await connectable.dispose()"
|
||||
echo ""
|
||||
echo "if context.is_offline_mode():"
|
||||
echo " run_migrations_offline()"
|
||||
echo "else:"
|
||||
echo " asyncio.run(run_migrations_online())"
|
||||
195
backend/scripts/seed_db.py
Normal file
195
backend/scripts/seed_db.py
Normal file
@@ -0,0 +1,195 @@
|
||||
"""Database seeding script for WealthWise.
|
||||
|
||||
This script populates the database with sample data for development and testing.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from datetime import date, timedelta
|
||||
from decimal import Decimal
|
||||
from uuid import uuid4
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
from app.core.db import AsyncSessionLocal, engine
|
||||
from app.core.security import get_password_hash
|
||||
from app.models import Portfolio, Transaction, User
|
||||
from app.models.transaction import TransactionType
|
||||
|
||||
|
||||
async def seed_database():
|
||||
"""Seed the database with sample data."""
|
||||
print("Starting database seeding...")
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
# Check if demo user already exists
|
||||
result = await session.execute(
|
||||
select(User).where(User.email == "demo@wealthwise.app")
|
||||
)
|
||||
existing_user = result.scalar_one_or_none()
|
||||
|
||||
if existing_user:
|
||||
print("Demo user already exists. Skipping seeding.")
|
||||
return
|
||||
|
||||
# Create demo user
|
||||
print("Creating demo user...")
|
||||
demo_user = User(
|
||||
id=uuid4(),
|
||||
email="demo@wealthwise.app",
|
||||
hashed_password=get_password_hash("password123"),
|
||||
is_active=True,
|
||||
is_superuser=False,
|
||||
)
|
||||
session.add(demo_user)
|
||||
await session.flush() # Flush to get the user ID
|
||||
print(f"Created user: {demo_user.email} (ID: {demo_user.id})")
|
||||
|
||||
# Create portfolio
|
||||
print("Creating portfolio...")
|
||||
portfolio = Portfolio(
|
||||
id=uuid4(),
|
||||
user_id=demo_user.id,
|
||||
name="Main Investment",
|
||||
description="Primary investment portfolio for tracking stocks, mutual funds, and other assets.",
|
||||
)
|
||||
session.add(portfolio)
|
||||
await session.flush()
|
||||
print(f"Created portfolio: {portfolio.name} (ID: {portfolio.id})")
|
||||
|
||||
# Create sample transactions
|
||||
print("Creating transactions...")
|
||||
transactions = [
|
||||
# Income transactions
|
||||
Transaction(
|
||||
id=uuid4(),
|
||||
portfolio_id=portfolio.id,
|
||||
amount=Decimal("75000.00"),
|
||||
transaction_type=TransactionType.INCOME,
|
||||
transaction_date=date.today() - timedelta(days=30),
|
||||
description="Monthly Salary",
|
||||
category="Salary",
|
||||
),
|
||||
Transaction(
|
||||
id=uuid4(),
|
||||
portfolio_id=portfolio.id,
|
||||
amount=Decimal("2500.00"),
|
||||
transaction_type=TransactionType.INCOME,
|
||||
transaction_date=date.today() - timedelta(days=25),
|
||||
description="Freelance Project Payment",
|
||||
category="Freelance",
|
||||
),
|
||||
Transaction(
|
||||
id=uuid4(),
|
||||
portfolio_id=portfolio.id,
|
||||
amount=Decimal("1200.00"),
|
||||
transaction_type=TransactionType.INCOME,
|
||||
transaction_date=date.today() - timedelta(days=20),
|
||||
description="Stock Dividend - TCS",
|
||||
category="Dividend",
|
||||
),
|
||||
# Expense transactions
|
||||
Transaction(
|
||||
id=uuid4(),
|
||||
portfolio_id=portfolio.id,
|
||||
amount=Decimal("25000.00"),
|
||||
transaction_type=TransactionType.EXPENSE,
|
||||
transaction_date=date.today() - timedelta(days=28),
|
||||
description="Monthly Rent",
|
||||
category="Housing",
|
||||
),
|
||||
Transaction(
|
||||
id=uuid4(),
|
||||
portfolio_id=portfolio.id,
|
||||
amount=Decimal("8000.00"),
|
||||
transaction_type=TransactionType.EXPENSE,
|
||||
transaction_date=date.today() - timedelta(days=26),
|
||||
description="Grocery Shopping - BigBasket",
|
||||
category="Groceries",
|
||||
),
|
||||
Transaction(
|
||||
id=uuid4(),
|
||||
portfolio_id=portfolio.id,
|
||||
amount=Decimal("3500.00"),
|
||||
transaction_type=TransactionType.EXPENSE,
|
||||
transaction_date=date.today() - timedelta(days=22),
|
||||
description="Electricity & Internet Bill",
|
||||
category="Utilities",
|
||||
),
|
||||
Transaction(
|
||||
id=uuid4(),
|
||||
portfolio_id=portfolio.id,
|
||||
amount=Decimal("5000.00"),
|
||||
transaction_type=TransactionType.EXPENSE,
|
||||
transaction_date=date.today() - timedelta(days=18),
|
||||
description="Dining Out & Entertainment",
|
||||
category="Entertainment",
|
||||
),
|
||||
# Investment transactions
|
||||
Transaction(
|
||||
id=uuid4(),
|
||||
portfolio_id=portfolio.id,
|
||||
amount=Decimal("10000.00"),
|
||||
transaction_type=TransactionType.TRANSFER,
|
||||
transaction_date=date.today() - timedelta(days=24),
|
||||
description="Stock Purchase - Infosys",
|
||||
category="Investment",
|
||||
),
|
||||
Transaction(
|
||||
id=uuid4(),
|
||||
portfolio_id=portfolio.id,
|
||||
amount=Decimal("5000.00"),
|
||||
transaction_type=TransactionType.TRANSFER,
|
||||
transaction_date=date.today() - timedelta(days=21),
|
||||
description="Mutual Fund SIP - SBI Bluechip",
|
||||
category="Investment",
|
||||
),
|
||||
Transaction(
|
||||
id=uuid4(),
|
||||
portfolio_id=portfolio.id,
|
||||
amount=Decimal("3000.00"),
|
||||
transaction_type=TransactionType.TRANSFER,
|
||||
transaction_date=date.today() - timedelta(days=15),
|
||||
description="Crypto Purchase - Bitcoin",
|
||||
category="Investment",
|
||||
),
|
||||
]
|
||||
|
||||
for transaction in transactions:
|
||||
session.add(transaction)
|
||||
|
||||
await session.commit()
|
||||
print(f"Created {len(transactions)} transactions")
|
||||
|
||||
# Calculate summary
|
||||
income_total = sum(t.amount for t in transactions if t.transaction_type == TransactionType.INCOME)
|
||||
expense_total = sum(t.amount for t in transactions if t.transaction_type == TransactionType.EXPENSE)
|
||||
transfer_total = sum(t.amount for t in transactions if t.transaction_type == TransactionType.TRANSFER)
|
||||
|
||||
print("\n" + "="*50)
|
||||
print("SEEDING COMPLETE!")
|
||||
print("="*50)
|
||||
print(f"User: {demo_user.email}")
|
||||
print(f"Password: password123")
|
||||
print(f"Portfolio: {portfolio.name}")
|
||||
print(f"\nTransaction Summary:")
|
||||
print(f" Total Income: ₹{income_total:,.2f}")
|
||||
print(f" Total Expenses: ₹{expense_total:,.2f}")
|
||||
print(f" Total Investments: ₹{transfer_total:,.2f}")
|
||||
print(f" Net Cash Flow: ₹{(income_total - expense_total):,.2f}")
|
||||
print("="*50)
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main entry point for seeding."""
|
||||
try:
|
||||
await seed_database()
|
||||
except Exception as e:
|
||||
print(f"Error seeding database: {e}")
|
||||
raise
|
||||
finally:
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user