121 lines
4.0 KiB
Python
121 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
First-run initialization script.
|
|
Creates the admin user and seeds default categories.
|
|
Run once after `flask db upgrade`.
|
|
|
|
Usage:
|
|
cd /home/pfm/app
|
|
source venv/bin/activate
|
|
python scripts/init_db.py
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
|
|
# Add project root to path
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from app import create_app
|
|
from app.extensions import db
|
|
from app.models.user import User
|
|
from app.models.category import Category
|
|
import getpass
|
|
|
|
app = create_app()
|
|
|
|
DEFAULT_EXPENSE_CATEGORIES = [
|
|
('Housing', 'bi-house', '#6366f1'),
|
|
('Food & Dining', 'bi-cup-hot', '#f59e0b'),
|
|
('Transport', 'bi-car-front', '#3b82f6'),
|
|
('Utilities', 'bi-lightning-charge', '#8b5cf6'),
|
|
('Health', 'bi-heart-pulse', '#ef4444'),
|
|
('Entertainment', 'bi-controller', '#ec4899'),
|
|
('Shopping', 'bi-bag', '#f97316'),
|
|
('Education', 'bi-book', '#14b8a6'),
|
|
('Insurance', 'bi-shield-check', '#64748b'),
|
|
('Personal Care', 'bi-person-heart', '#a78bfa'),
|
|
('Travel', 'bi-airplane', '#0ea5e9'),
|
|
('Subscriptions', 'bi-repeat', '#84cc16'),
|
|
('Gifts', 'bi-gift', '#f43f5e'),
|
|
('Other', 'bi-three-dots', '#94a3b8'),
|
|
]
|
|
|
|
DEFAULT_INCOME_CATEGORIES = [
|
|
('Salary', 'bi-briefcase', '#10b981'),
|
|
('Freelance', 'bi-laptop', '#06b6d4'),
|
|
('Business', 'bi-building', '#8b5cf6'),
|
|
('Investment', 'bi-graph-up-arrow', '#3b82f6'),
|
|
('Rental', 'bi-house-door', '#f59e0b'),
|
|
('Gift Received', 'bi-gift', '#ec4899'),
|
|
('Other Income', 'bi-three-dots', '#94a3b8'),
|
|
]
|
|
|
|
|
|
def seed_categories():
|
|
print("Seeding categories...")
|
|
count = 0
|
|
for name, icon, color in DEFAULT_EXPENSE_CATEGORIES:
|
|
if not Category.query.filter_by(name=name, category_type='expense').first():
|
|
db.session.add(Category(
|
|
name=name, icon=icon, color=color,
|
|
category_type='expense', is_system=True
|
|
))
|
|
count += 1
|
|
|
|
for name, icon, color in DEFAULT_INCOME_CATEGORIES:
|
|
if not Category.query.filter_by(name=name, category_type='income').first():
|
|
db.session.add(Category(
|
|
name=name, icon=icon, color=color,
|
|
category_type='income', is_system=True
|
|
))
|
|
count += 1
|
|
|
|
db.session.commit()
|
|
print(f" → {count} categories added.")
|
|
|
|
|
|
def create_admin():
|
|
print("\nCreate admin user")
|
|
print("-" * 30)
|
|
|
|
existing = User.query.first()
|
|
if existing:
|
|
print(f" User '{existing.username}' already exists. Skipping.")
|
|
return
|
|
|
|
username = input("Username [admin]: ").strip() or 'admin'
|
|
display_name = input("Display name (optional): ").strip() or None
|
|
email = input("Email (optional): ").strip() or None
|
|
|
|
while True:
|
|
password = getpass.getpass("Password: ")
|
|
confirm = getpass.getpass("Confirm password: ")
|
|
if password == confirm and len(password) >= 6:
|
|
break
|
|
if password != confirm:
|
|
print(" Passwords do not match. Try again.")
|
|
else:
|
|
print(" Password must be at least 6 characters.")
|
|
|
|
from flask import current_app
|
|
user = User(
|
|
username=username,
|
|
display_name=display_name,
|
|
email=email,
|
|
currency=current_app.config.get('APP_CURRENCY', 'USD'),
|
|
currency_symbol=current_app.config.get('APP_CURRENCY_SYMBOL', '$'),
|
|
timezone=current_app.config.get('APP_TIMEZONE', 'Asia/Ho_Chi_Minh'),
|
|
)
|
|
user.set_password(password)
|
|
db.session.add(user)
|
|
db.session.commit()
|
|
print(f" → User '{username}' created successfully.")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
with app.app_context():
|
|
seed_categories()
|
|
create_admin()
|
|
print("\nInit complete. Run the app with: gunicorn wsgi:app")
|