mohsin-devs Cursor commited on
Commit
612435b
Β·
1 Parent(s): 6a71f57

fix: prevent HF 502 by removing sklearn import at startup and hardening DB init

Browse files
backend/app/database/database.py CHANGED
@@ -12,7 +12,11 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__fil
12
  sqlite_db_path = os.path.join(BASE_DIR, "bankbot.db")
13
 
14
  if USE_SQLITE:
15
- SQLALCHEMY_DATABASE_URL = f"sqlite:///{sqlite_db_path}"
 
 
 
 
16
 
17
  connect_args = {}
18
  if "sqlite" in SQLALCHEMY_DATABASE_URL:
 
12
  sqlite_db_path = os.path.join(BASE_DIR, "bankbot.db")
13
 
14
  if USE_SQLITE:
15
+ env_db = os.getenv("DATABASE_URL", "")
16
+ if env_db.startswith("sqlite:"):
17
+ SQLALCHEMY_DATABASE_URL = env_db
18
+ else:
19
+ SQLALCHEMY_DATABASE_URL = f"sqlite:///{sqlite_db_path}"
20
 
21
  connect_args = {}
22
  if "sqlite" in SQLALCHEMY_DATABASE_URL:
backend/app/loans/router.py CHANGED
@@ -6,7 +6,7 @@ from sqlalchemy.orm import Session
6
 
7
  from app.database.database import get_db
8
  from app.database.models import User
9
- from app.ai.loan_predictor import calculate_loan_eligibility, generate_loan_comparison
10
 
11
  router = APIRouter(prefix="/api/loans", tags=["Loans"])
12
 
@@ -36,7 +36,7 @@ def loan_eligibility(
36
  db: Session = Depends(get_db),
37
  ):
38
  _resolve_user(db, user_id)
39
- result = calculate_loan_eligibility(
40
  body.salary,
41
  body.credit_score,
42
  body.existing_loans,
@@ -44,8 +44,6 @@ def loan_eligibility(
44
  body.age,
45
  body.loan_amount,
46
  )
47
- comparison = generate_loan_comparison(body.loan_amount)
48
- return {**result, "comparison": comparison[:12]}
49
 
50
 
51
  @router.get("/comparison")
@@ -55,4 +53,5 @@ def loan_comparison(
55
  db: Session = Depends(get_db),
56
  ):
57
  _resolve_user(db, user_id)
58
- return {"loan_amount": loan_amount, "comparison": generate_loan_comparison(loan_amount)}
 
 
6
 
7
  from app.database.database import get_db
8
  from app.database.models import User
9
+ from app.loans.service import analyze_loan
10
 
11
  router = APIRouter(prefix="/api/loans", tags=["Loans"])
12
 
 
36
  db: Session = Depends(get_db),
37
  ):
38
  _resolve_user(db, user_id)
39
+ return analyze_loan(
40
  body.salary,
41
  body.credit_score,
42
  body.existing_loans,
 
44
  body.age,
45
  body.loan_amount,
46
  )
 
 
47
 
48
 
49
  @router.get("/comparison")
 
53
  db: Session = Depends(get_db),
54
  ):
55
  _resolve_user(db, user_id)
56
+ result = analyze_loan(85000, 720, 1, 4, 32, loan_amount)
57
+ return {"loan_amount": loan_amount, "comparison": result["comparison"]}
backend/app/loans/service.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Loan eligibility without heavy ML deps (safe for HF Docker)."""
2
+
3
+
4
+ def calculate_emi(principal: float, rate_percent: float, tenure_years: int) -> float:
5
+ if rate_percent <= 0:
6
+ months = tenure_years * 12
7
+ return principal / months if months else principal
8
+ monthly_rate = rate_percent / 100 / 12
9
+ months = tenure_years * 12
10
+ if monthly_rate == 0:
11
+ return principal / months
12
+ return principal * monthly_rate * ((1 + monthly_rate) ** months) / (((1 + monthly_rate) ** months) - 1)
13
+
14
+
15
+ def analyze_loan(
16
+ salary: float,
17
+ credit_score: int,
18
+ existing_loans: int,
19
+ employment_years: float,
20
+ age: int,
21
+ loan_amount: float,
22
+ ) -> dict:
23
+ issues: list[str] = []
24
+ score = 50.0
25
+
26
+ if credit_score >= 750:
27
+ score += 20
28
+ elif credit_score >= 700:
29
+ score += 12
30
+ elif credit_score >= 650:
31
+ score += 5
32
+ else:
33
+ score -= 10
34
+ issues.append("Credit score below 650 β€” consider improving before applying")
35
+
36
+ if employment_years >= 3:
37
+ score += 10
38
+ elif employment_years < 1:
39
+ score -= 8
40
+ issues.append("Less than 1 year employment β€” higher scrutiny expected")
41
+
42
+ if age < 21 or age > 65:
43
+ issues.append("Age outside typical lending range (21–65)")
44
+
45
+ if existing_loans > 3:
46
+ score -= 12
47
+ issues.append("Multiple existing loans may reduce approval odds")
48
+
49
+ emi = calculate_emi(loan_amount, 12, 10)
50
+ monthly_salary = salary / 12
51
+ emi_ratio = (emi / monthly_salary * 100) if monthly_salary > 0 else 100
52
+
53
+ if emi_ratio > 50:
54
+ score -= 15
55
+ issues.append(f"EMI is {emi_ratio:.0f}% of monthly income β€” reduce loan amount")
56
+ elif emi_ratio < 35:
57
+ score += 8
58
+
59
+ if loan_amount > salary * 5:
60
+ score -= 10
61
+ issues.append("Loan amount exceeds 5Γ— annual salary")
62
+
63
+ score = max(0, min(100, score))
64
+ approval_probability = score
65
+
66
+ if approval_probability >= 70:
67
+ status = "APPROVED"
68
+ risk = "Low"
69
+ elif approval_probability >= 45:
70
+ status = "UNDER REVIEW"
71
+ risk = "Medium"
72
+ else:
73
+ status = "LIKELY REJECTED"
74
+ risk = "High"
75
+
76
+ recommendations = []
77
+ if approval_probability >= 70:
78
+ recommendations.append("Strong profile β€” you are likely to qualify at competitive rates")
79
+ elif approval_probability < 45:
80
+ recommendations.append("Consider a smaller loan or improving credit score before applying")
81
+ if emi_ratio < 35:
82
+ recommendations.append(f"Healthy EMI ratio ({emi_ratio:.0f}% of monthly income)")
83
+ elif emi_ratio > 40:
84
+ recommendations.append("Try a longer tenure or lower amount to reduce monthly EMI")
85
+
86
+ comparison = []
87
+ for rate in (9, 10, 11, 12):
88
+ for tenure in (5, 7, 10):
89
+ e = calculate_emi(loan_amount, rate, tenure)
90
+ total = e * 12 * tenure
91
+ comparison.append({
92
+ "rate": f"{rate}%",
93
+ "tenure": f"{tenure} years",
94
+ "emi": round(e, 2),
95
+ "total_amount": round(total, 2),
96
+ "interest": round(total - loan_amount, 2),
97
+ })
98
+
99
+ return {
100
+ "approval_probability": round(approval_probability, 1),
101
+ "approval_status": status,
102
+ "risk_level": risk,
103
+ "loan_score": round(score, 1),
104
+ "emi": round(emi, 2),
105
+ "monthly_emi": round(emi, 2),
106
+ "issues": issues,
107
+ "recommendations": recommendations,
108
+ "comparison": comparison[:12],
109
+ }
backend/app/main.py CHANGED
@@ -111,8 +111,11 @@ async def rate_limit(request: Request, call_next):
111
  def startup():
112
  api_logger.info("BankBot API starting", extra={"version": "2.0.0"})
113
  Base.metadata.create_all(bind=engine)
114
- from app.database.chat_migrate import migrate_chat_schema
115
- migrate_chat_schema()
 
 
 
116
  api_logger.info("Database tables ready")
117
 
118
  # Log active backends
 
111
  def startup():
112
  api_logger.info("BankBot API starting", extra={"version": "2.0.0"})
113
  Base.metadata.create_all(bind=engine)
114
+ try:
115
+ from app.database.chat_migrate import migrate_chat_schema
116
+ migrate_chat_schema()
117
+ except Exception as exc:
118
+ api_logger.warning("Chat schema migrate skipped", extra={"error": str(exc)[:200]})
119
  api_logger.info("Database tables ready")
120
 
121
  # Log active backends
hf/start.sh CHANGED
@@ -3,7 +3,7 @@
3
  # BankBot AI β€” Hugging Face Spaces startup script
4
  # Runs: DB init β†’ optional seed β†’ supervisord (nginx + fastapi + nextjs)
5
  # ============================================================
6
- set -e
7
 
8
  echo "============================================"
9
  echo " BankBot AI β€” Starting on Hugging Face"
@@ -57,9 +57,11 @@ cd /app/backend
57
  python -c "
58
  from app.database.database import engine, Base
59
  import app.database.models
 
60
  Base.metadata.create_all(bind=engine)
 
61
  print(' Database tables ready')
62
- "
63
 
64
  # ── Seed demo data (only if DB is empty) ─────────────────────────────────────
65
  echo "[2/3] Checking demo data..."
@@ -87,7 +89,7 @@ if count == 0:
87
  print(' Demo account ready: alex@bankbot.dev / BankBot2026!')
88
  else:
89
  print(f' Database has {count} users β€” skipping seed')
90
- "
91
 
92
  # ── Start all services via supervisord ───────────────────────────────────────
93
  echo "[3/3] Starting services (Nginx + FastAPI + Next.js)..."
 
3
  # BankBot AI β€” Hugging Face Spaces startup script
4
  # Runs: DB init β†’ optional seed β†’ supervisord (nginx + fastapi + nextjs)
5
  # ============================================================
6
+ set -euo pipefail
7
 
8
  echo "============================================"
9
  echo " BankBot AI β€” Starting on Hugging Face"
 
57
  python -c "
58
  from app.database.database import engine, Base
59
  import app.database.models
60
+ from app.database.chat_migrate import migrate_chat_schema
61
  Base.metadata.create_all(bind=engine)
62
+ migrate_chat_schema()
63
  print(' Database tables ready')
64
+ " || { echo "[WARN] DB init had errors β€” continuing"; }
65
 
66
  # ── Seed demo data (only if DB is empty) ─────────────────────────────────────
67
  echo "[2/3] Checking demo data..."
 
89
  print(' Demo account ready: alex@bankbot.dev / BankBot2026!')
90
  else:
91
  print(f' Database has {count} users β€” skipping seed')
92
+ " || echo "[WARN] Seed check failed β€” continuing"
93
 
94
  # ── Start all services via supervisord ───────────────────────────────────────
95
  echo "[3/3] Starting services (Nginx + FastAPI + Next.js)..."