06/03 Optimize app
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Schwab daily auto-sync — balance, positions, and transactions.
|
||||
|
||||
Add to crontab (crontab -e):
|
||||
# Sync Schwab every day at 7 AM
|
||||
0 7 * * * /home/pfm/venv/bin/python /home/pfm/web/scripts/sync_schwab.py >> /home/pfm/web/logs/sync_schwab.log 2>&1
|
||||
|
||||
Or run manually:
|
||||
python scripts/sync_schwab.py
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from app import create_app
|
||||
from app.extensions import db
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s|%(levelname)s|%(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S',
|
||||
)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
app = create_app()
|
||||
|
||||
with app.app_context():
|
||||
from app.models.schwab_connection import SchwabConnection, SchwabAccount
|
||||
from app.services.schwab_service import (
|
||||
sync_account_snapshot, sync_preview, import_transactions,
|
||||
)
|
||||
|
||||
connection = SchwabConnection.query.filter_by(is_active=True).first()
|
||||
if not connection:
|
||||
log.warning('[sync_schwab] No active Schwab connection — skipping.')
|
||||
sys.exit(0)
|
||||
|
||||
# Warn if refresh token is close to expiry
|
||||
if connection.refresh_token_expires_at:
|
||||
days_left = (connection.refresh_token_expires_at - datetime.utcnow()).days
|
||||
if days_left <= 2:
|
||||
log.warning('[sync_schwab] Schwab refresh token expires in %d day(s)! '
|
||||
'Log in and reconnect at /schwab/connect.', days_left)
|
||||
|
||||
accounts = SchwabAccount.query.filter(
|
||||
SchwabAccount.pfm_account_id != None,
|
||||
SchwabAccount.is_active == True,
|
||||
).all()
|
||||
|
||||
if not accounts:
|
||||
log.warning('[sync_schwab] No mapped Schwab accounts found.')
|
||||
sys.exit(0)
|
||||
|
||||
log.info('[sync_schwab] Syncing %d account(s)…', len(accounts))
|
||||
|
||||
for sa in accounts:
|
||||
sa.connection = connection # always use active connection
|
||||
|
||||
# ── 1. Balance + positions ────────────────────────────────────────────
|
||||
try:
|
||||
bal_updated, pos_synced = sync_account_snapshot(sa)
|
||||
log.info('[sync_schwab] %s — balance updated=%s positions=%d',
|
||||
sa.account_name, bal_updated, pos_synced)
|
||||
except Exception as e:
|
||||
log.error('[sync_schwab] snapshot failed for %s: %s', sa.account_name, e,
|
||||
exc_info=True)
|
||||
|
||||
# ── 2. New transactions ───────────────────────────────────────────────
|
||||
try:
|
||||
preview = sync_preview(sa)
|
||||
if preview:
|
||||
imported, skipped = import_transactions(preview, sa)
|
||||
log.info('[sync_schwab] %s — transactions: imported=%d skipped=%d',
|
||||
sa.account_name, imported, skipped)
|
||||
else:
|
||||
log.info('[sync_schwab] %s — no new transactions.', sa.account_name)
|
||||
except Exception as e:
|
||||
log.error('[sync_schwab] transaction sync failed for %s: %s',
|
||||
sa.account_name, e, exc_info=True)
|
||||
|
||||
log.info('[sync_schwab] Done.')
|
||||
Reference in New Issue
Block a user