omgy commited on
Commit
8f5c3f8
·
verified ·
1 Parent(s): 0234942

Update app/data/mock_profiles.py

Browse files
Files changed (1) hide show
  1. app/data/mock_profiles.py +180 -226
app/data/mock_profiles.py CHANGED
@@ -1,250 +1,204 @@
1
  """
2
- Authentication router for user login and registration.
3
- Handles Firebase Authentication integration.
4
  """
5
 
6
- from datetime import datetime
7
- from typing import Optional
8
-
9
- from app.config import settings
10
- from app.schemas import (
11
- ErrorResponse,
12
- LoginRequest,
13
- LoginResponse,
14
- MessageResponse,
15
- RegisterRequest,
16
- )
17
- from app.services.firebase_service import firebase_service
18
- from app.utils.logger import default_logger as logger
19
- from fastapi import APIRouter, Header, HTTPException, status
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
- router = APIRouter()
 
 
 
22
 
23
 
24
- @router.post("/login", response_model=LoginResponse)
25
- async def login(request: LoginRequest):
26
  """
27
- Login endpoint for user authentication.
28
-
29
- For hackathon: Simple email/password validation.
30
- In production: This would validate Firebase ID tokens.
31
 
32
  Args:
33
- request: Login request with email and password
34
 
35
  Returns:
36
- LoginResponse with access token and user info
37
  """
38
- try:
39
- logger.info(f"Login attempt for email: {request.email}")
40
-
41
- # For hackathon: Simple validation
42
- if len(request.password) < 6:
43
- raise HTTPException(
44
- status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials"
45
- )
46
-
47
- # Try to find user by email in Firestore
48
- # In production, Firebase Auth would handle this
49
- user_profile = None
50
-
51
- # For now, we'll check if user exists or create a mock response
52
- # In production, you'd verify Firebase ID token here
53
-
54
- # Generate a mock user_id from email (for demo)
55
- user_id = request.email.split("@")[0].replace(".", "_")
56
-
57
- # Try to fetch existing user
58
- user_profile = firebase_service.get_user_profile(user_id)
59
-
60
- if not user_profile:
61
- # For demo: Create a profile with realistic mock data
62
- logger.info(f"Creating new user profile with mock data for {request.email}")
63
-
64
- # Assign random mock profile with realistic financial data
65
- basic_user_data = {
66
- "user_id": user_id,
67
- "email": request.email,
68
- "full_name": request.email.split("@")[0].title(),
69
- }
70
- user_profile = assign_mock_profile_to_user(basic_user_data)
71
-
72
- firebase_service.create_user_profile(user_profile)
73
- logger.info(
74
- f"Assigned mock profile: Credit Score={user_profile.get('mock_credit_score')}, Income=₹{user_profile.get('monthly_income')}"
75
- )
76
-
77
- # Generate access token (in production, use proper JWT)
78
- access_token = f"finagent_token_{user_id}_{datetime.utcnow().timestamp()}"
79
-
80
- response = LoginResponse(
81
- access_token=access_token,
82
- token_type="Bearer",
83
- user_id=user_profile.get("user_id", user_id),
84
- full_name=user_profile.get("full_name", "User"),
85
- email=user_profile.get("email", request.email),
86
- )
87
-
88
- logger.info(f"Login successful for user: {user_id}")
89
- return response
90
-
91
- except HTTPException:
92
- raise
93
- except Exception as e:
94
- logger.error(f"Login error: {str(e)}")
95
- raise HTTPException(
96
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Login failed"
97
- )
98
-
99
-
100
- @router.post("/register", response_model=LoginResponse)
101
- async def register(request: RegisterRequest):
102
- """
103
- Register a new user.
104
 
105
- Args:
106
- request: Registration request with user details
107
 
108
- Returns:
109
- LoginResponse with access token and user info
110
- """
111
- try:
112
- logger.info(f"Registration attempt for email: {request.email}")
113
-
114
- # Generate user_id from email
115
- user_id = request.email.split("@")[0].replace(".", "_")
116
-
117
- # Check if user already exists
118
- existing_user = firebase_service.get_user_profile(user_id)
119
- if existing_user:
120
- raise HTTPException(
121
- status_code=status.HTTP_400_BAD_REQUEST, detail="User already exists"
122
- )
123
-
124
- # Create user profile with mock financial data
125
- basic_user_data = {
126
- "user_id": user_id,
127
- "email": request.email,
128
- "full_name": request.full_name,
129
- "monthly_income": request.monthly_income
130
- if hasattr(request, "monthly_income")
131
- else None,
132
- "existing_emi": request.existing_emi
133
- if hasattr(request, "existing_emi")
134
- else None,
135
- }
136
-
137
- # Assign mock profile with realistic data (KYC, CIBIL, etc.)
138
- user_profile = assign_mock_profile_to_user(basic_user_data)
139
-
140
- logger.info(
141
- f"Assigning mock profile to new user: Credit Score={user_profile.get('mock_credit_score')}, Income=₹{user_profile.get('monthly_income')}"
142
- )
143
-
144
- created_profile = firebase_service.create_user_profile(user_profile)
145
-
146
- # Generate access token
147
- access_token = f"finagent_token_{user_id}_{datetime.utcnow().timestamp()}"
148
-
149
- response = LoginResponse(
150
- access_token=access_token,
151
- token_type="Bearer",
152
- user_id=created_profile.get("user_id", user_id),
153
- full_name=created_profile.get("full_name"),
154
- email=created_profile.get("email"),
155
- )
156
-
157
- logger.info(f"Registration successful for user: {user_id}")
158
- return response
159
-
160
- except HTTPException:
161
- raise
162
- except Exception as e:
163
- logger.error(f"Registration error: {str(e)}")
164
- raise HTTPException(
165
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
166
- detail="Registration failed",
167
- )
168
-
169
-
170
- @router.post("/verify-token")
171
- async def verify_token(authorization: Optional[str] = Header(None)):
172
  """
173
- Verify Firebase ID token.
174
-
175
- Args:
176
- authorization: Authorization header with Bearer token
177
 
178
  Returns:
179
- Token verification result
180
  """
181
- try:
182
- if not authorization:
183
- raise HTTPException(
184
- status_code=status.HTTP_401_UNAUTHORIZED,
185
- detail="Authorization header missing",
186
- )
187
-
188
- # Extract token from Bearer scheme
189
- parts = authorization.split()
190
- if len(parts) != 2 or parts[0].lower() != "bearer":
191
- raise HTTPException(
192
- status_code=status.HTTP_401_UNAUTHORIZED,
193
- detail="Invalid authorization header format",
194
- )
195
-
196
- token = parts[1]
197
-
198
- # For hackathon: Simple token validation
199
- if token.startswith("finagent_token_"):
200
- # Extract user_id from token
201
- parts = token.split("_")
202
- if len(parts) >= 3:
203
- user_id = "_".join(parts[2:-1])
204
- return MessageResponse(message="Token valid", success=True)
205
-
206
- # In production: Verify Firebase ID token
207
- # decoded_token = firebase_service.verify_token(token)
208
- # if decoded_token:
209
- # return MessageResponse(message="Token valid", success=True)
210
-
211
- raise HTTPException(
212
- status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired token"
213
- )
214
-
215
- except HTTPException:
216
- raise
217
- except Exception as e:
218
- logger.error(f"Token verification error: {str(e)}")
219
- raise HTTPException(
220
- status_code=status.HTTP_401_UNAUTHORIZED, detail="Token verification failed"
221
- )
222
-
223
-
224
- @router.post("/logout")
225
- async def logout(authorization: Optional[str] = Header(None)):
226
- """
227
- Logout endpoint.
228
 
229
- For stateless JWT tokens, this is mainly for client-side cleanup.
230
- In production with Firebase, you might want to revoke refresh tokens.
 
231
 
232
  Args:
233
- authorization: Authorization header with Bearer token
234
 
235
  Returns:
236
- Success message
237
  """
238
- try:
239
- logger.info("Logout request received")
240
-
241
- # In production: Could revoke Firebase refresh tokens here
242
- # or add token to blacklist
243
-
244
- return MessageResponse(message="Logged out successfully", success=True)
245
-
246
- except Exception as e:
247
- logger.error(f"Logout error: {str(e)}")
248
- raise HTTPException(
249
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Logout failed"
250
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ Mock user profiles for automatic assignment on signup.
3
+ These profiles contain realistic financial data for loan processing demo.
4
  """
5
 
6
+ import random
7
+ from typing import Any, Dict, List
8
+
9
+ # Profile 1: Young Professional (Good Credit, High Approval Chance)
10
+ PROFILE_YOUNG_PROFESSIONAL = {
11
+ "monthly_income": 75000.0,
12
+ "existing_emi": 8000.0,
13
+ "mock_credit_score": 750,
14
+ "segment": "Salaried",
15
+ "employment_type": "Salaried",
16
+ "employment_years": 3,
17
+ "company_category": "Category A",
18
+ "pan_number": "ABCDE1234F",
19
+ "aadhar_number": "1234-5678-9012",
20
+ "address": "123, Tech Park, Bangalore, Karnataka - 560001",
21
+ "phone": "+91-9876543210",
22
+ "date_of_birth": "1995-06-15",
23
+ "gender": "Male",
24
+ "marital_status": "Single",
25
+ "current_loan_outstanding": 200000.0,
26
+ "bank_account_number": "1234567890",
27
+ "bank_name": "HDFC Bank",
28
+ "bank_ifsc": "HDFC0001234",
29
+ # KYC Documents (mocked)
30
+ "kyc_verified": True,
31
+ "kyc_status": "VERIFIED",
32
+ "cibil_score": 750,
33
+ "cibil_last_updated": "2024-11-15",
34
+ # Loan eligibility metrics
35
+ "max_eligible_amount": 500000.0,
36
+ "risk_category": "Low Risk",
37
+ "profile_completeness": 100,
38
+ "description": "Young professional with good credit score and stable income. High loan approval probability.",
39
+ }
40
+
41
+ # Profile 2: Mid-Career Professional (Average Credit, Moderate EMI)
42
+ PROFILE_MID_CAREER = {
43
+ "monthly_income": 50000.0,
44
+ "existing_emi": 12000.0,
45
+ "mock_credit_score": 680,
46
+ "segment": "Salaried",
47
+ "employment_type": "Salaried",
48
+ "employment_years": 7,
49
+ "company_category": "Category B",
50
+ "pan_number": "FGHIJ5678K",
51
+ "aadhar_number": "9876-5432-1098",
52
+ "address": "456, Green Avenue, Pune, Maharashtra - 411001",
53
+ "phone": "+91-9876543211",
54
+ "date_of_birth": "1990-03-22",
55
+ "gender": "Female",
56
+ "marital_status": "Married",
57
+ "current_loan_outstanding": 350000.0,
58
+ "bank_account_number": "2345678901",
59
+ "bank_name": "ICICI Bank",
60
+ "bank_ifsc": "ICIC0002345",
61
+ # KYC Documents (mocked)
62
+ "kyc_verified": True,
63
+ "kyc_status": "VERIFIED",
64
+ "cibil_score": 680,
65
+ "cibil_last_updated": "2024-10-20",
66
+ # Loan eligibility metrics
67
+ "max_eligible_amount": 300000.0,
68
+ "risk_category": "Medium Risk",
69
+ "profile_completeness": 95,
70
+ "description": "Mid-career professional with moderate existing EMI. May need loan amount adjustment.",
71
+ }
72
+
73
+ # Profile 3: Entry-Level Professional (Lower Credit, New to Credit)
74
+ PROFILE_ENTRY_LEVEL = {
75
+ "monthly_income": 35000.0,
76
+ "existing_emi": 3000.0,
77
+ "mock_credit_score": 650,
78
+ "segment": "New to Credit",
79
+ "employment_type": "Salaried",
80
+ "employment_years": 1,
81
+ "company_category": "Category B",
82
+ "pan_number": "KLMNO9012P",
83
+ "aadhar_number": "5555-6666-7777",
84
+ "address": "789, Lake View, Hyderabad, Telangana - 500001",
85
+ "phone": "+91-9876543212",
86
+ "date_of_birth": "1998-09-10",
87
+ "gender": "Male",
88
+ "marital_status": "Single",
89
+ "current_loan_outstanding": 50000.0,
90
+ "bank_account_number": "3456789012",
91
+ "bank_name": "SBI",
92
+ "bank_ifsc": "SBIN0003456",
93
+ # KYC Documents (mocked)
94
+ "kyc_verified": True,
95
+ "kyc_status": "VERIFIED",
96
+ "cibil_score": 650,
97
+ "cibil_last_updated": "2024-11-01",
98
+ # Loan eligibility metrics
99
+ "max_eligible_amount": 200000.0,
100
+ "risk_category": "High Risk",
101
+ "profile_completeness": 90,
102
+ "description": "Entry-level professional with limited credit history. Eligible for smaller loan amounts.",
103
+ }
104
+
105
+ # List of all profiles for random selection
106
+ MOCK_PROFILES: List[Dict[str, Any]] = [
107
+ PROFILE_YOUNG_PROFESSIONAL,
108
+ PROFILE_MID_CAREER,
109
+ PROFILE_ENTRY_LEVEL,
110
+ ]
111
+
112
+
113
+ def get_random_mock_profile() -> Dict[str, Any]:
114
+ """
115
+ Get a random mock profile from the available profiles.
116
 
117
+ Returns:
118
+ Dictionary with mock user financial data
119
+ """
120
+ return random.choice(MOCK_PROFILES).copy()
121
 
122
 
123
+ def get_profile_by_index(index: int) -> Dict[str, Any]:
 
124
  """
125
+ Get a specific mock profile by index.
 
 
 
126
 
127
  Args:
128
+ index: Profile index (0-2)
129
 
130
  Returns:
131
+ Dictionary with mock user financial data
132
  """
133
+ if 0 <= index < len(MOCK_PROFILES):
134
+ return MOCK_PROFILES[index].copy()
135
+ return MOCK_PROFILES[0].copy()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
 
 
137
 
138
+ def get_all_profiles() -> List[Dict[str, Any]]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  """
140
+ Get all available mock profiles.
 
 
 
141
 
142
  Returns:
143
+ List of all mock profile dictionaries
144
  """
145
+ return [profile.copy() for profile in MOCK_PROFILES]
146
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
 
148
+ def assign_mock_profile_to_user(user_data: Dict[str, Any]) -> Dict[str, Any]:
149
+ """
150
+ Assign a random mock profile to a new user, preserving their basic info.
151
 
152
  Args:
153
+ user_data: User's basic info (user_id, email, full_name)
154
 
155
  Returns:
156
+ Complete user profile with mock financial data
157
  """
158
+ # Get random profile
159
+ mock_profile = get_random_mock_profile()
160
+
161
+ # Merge user's actual data with mock profile
162
+ complete_profile = {
163
+ **mock_profile, # Start with mock financial data
164
+ "user_id": user_data.get("user_id"),
165
+ "email": user_data.get("email"),
166
+ "full_name": user_data.get("full_name"),
167
+ }
168
+
169
+ # If user provided any financial data during signup, use that instead
170
+ if "monthly_income" in user_data and user_data["monthly_income"]:
171
+ complete_profile["monthly_income"] = user_data["monthly_income"]
172
+ if "existing_emi" in user_data and user_data["existing_emi"]:
173
+ complete_profile["existing_emi"] = user_data["existing_emi"]
174
+
175
+ return complete_profile
176
+
177
+
178
+ # Profile descriptions for admin/display purposes
179
+ PROFILE_DESCRIPTIONS = {
180
+ "YOUNG_PROFESSIONAL": {
181
+ "name": "Young Professional",
182
+ "income_range": "₹70,000 - ₹80,000",
183
+ "credit_score_range": "740-760",
184
+ "approval_rate": "95%",
185
+ "max_loan": "₹5,00,000",
186
+ "typical_decision": "APPROVED",
187
+ },
188
+ "MID_CAREER": {
189
+ "name": "Mid-Career Professional",
190
+ "income_range": "₹45,000 - ₹55,000",
191
+ "credit_score_range": "670-690",
192
+ "approval_rate": "75%",
193
+ "max_loan": "₹3,00,000",
194
+ "typical_decision": "APPROVED or ADJUST",
195
+ },
196
+ "ENTRY_LEVEL": {
197
+ "name": "Entry-Level Professional",
198
+ "income_range": "₹30,000 - ₹40,000",
199
+ "credit_score_range": "640-660",
200
+ "approval_rate": "60%",
201
+ "max_loan": "₹2,00,000",
202
+ "typical_decision": "APPROVED (smaller amounts) or ADJUST",
203
+ },
204
+ }