Spaces:
Sleeping
Sleeping
feat:added profile service
Browse files- src/auth/router.py +26 -2
- src/profile/router.py +124 -26
- src/profile/schemas.py +23 -0
src/auth/router.py
CHANGED
|
@@ -11,7 +11,8 @@ from src.auth.service import (
|
|
| 11 |
login_user,
|
| 12 |
)
|
| 13 |
from src.auth.utils import get_current_user
|
| 14 |
-
from src.core.models import Users
|
|
|
|
| 15 |
from src.core.config import settings
|
| 16 |
from fastapi.responses import RedirectResponse
|
| 17 |
from .schemas import SignUpRequest, LoginRequest, BaseResponse, SendVerificationRequest
|
|
@@ -63,6 +64,21 @@ async def login(
|
|
| 63 |
payload: LoginRequest, session: AsyncSession = Depends(get_async_session)
|
| 64 |
):
|
| 65 |
response = await login_user(session, payload.email, payload.password)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
return {"code": 200, "data": response}
|
| 67 |
|
| 68 |
|
|
@@ -104,6 +120,13 @@ async def get_home(
|
|
| 104 |
if not user:
|
| 105 |
raise HTTPException(status_code=404, detail="User not found")
|
| 106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
# Example payload — replace with your real app data
|
| 108 |
return {
|
| 109 |
"code": 200,
|
|
@@ -115,7 +138,8 @@ async def get_home(
|
|
| 115 |
"email": user.email_id,
|
| 116 |
"is_verified": user.is_verified,
|
| 117 |
"dob": user.dob.isoformat() if user.dob else None,
|
| 118 |
-
"profile_picture": user.profile_picture
|
|
|
|
| 119 |
},
|
| 120 |
"home_data": {
|
| 121 |
"announcements": ["Welcome!", "New protocol released"],
|
|
|
|
| 11 |
login_user,
|
| 12 |
)
|
| 13 |
from src.auth.utils import get_current_user
|
| 14 |
+
from src.core.models import Users, Roles, UserTeamsRole
|
| 15 |
+
from sqlmodel import select
|
| 16 |
from src.core.config import settings
|
| 17 |
from fastapi.responses import RedirectResponse
|
| 18 |
from .schemas import SignUpRequest, LoginRequest, BaseResponse, SendVerificationRequest
|
|
|
|
| 64 |
payload: LoginRequest, session: AsyncSession = Depends(get_async_session)
|
| 65 |
):
|
| 66 |
response = await login_user(session, payload.email, payload.password)
|
| 67 |
+
|
| 68 |
+
user_id = response["user"]["id"]
|
| 69 |
+
|
| 70 |
+
# 🔥 Fetch User Role
|
| 71 |
+
result = await session.exec(
|
| 72 |
+
select(Roles)
|
| 73 |
+
.join(UserTeamsRole, UserTeamsRole.role_id == Roles.id)
|
| 74 |
+
.where(UserTeamsRole.user_id == uuid.UUID(user_id))
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
role_obj = result.first()
|
| 78 |
+
role_name = role_obj.name if role_obj else "Member"
|
| 79 |
+
|
| 80 |
+
response["user"]["role"] = (role_name or "member").lower()
|
| 81 |
+
|
| 82 |
return {"code": 200, "data": response}
|
| 83 |
|
| 84 |
|
|
|
|
| 120 |
if not user:
|
| 121 |
raise HTTPException(status_code=404, detail="User not found")
|
| 122 |
|
| 123 |
+
role_join = await session.exec(
|
| 124 |
+
select(Roles.name)
|
| 125 |
+
.join(UserTeamsRole, UserTeamsRole.role_id == Roles.id)
|
| 126 |
+
.where(UserTeamsRole.user_id == uuid.UUID(user_id))
|
| 127 |
+
)
|
| 128 |
+
user_role = role_join.first() or "Member"
|
| 129 |
+
|
| 130 |
# Example payload — replace with your real app data
|
| 131 |
return {
|
| 132 |
"code": 200,
|
|
|
|
| 138 |
"email": user.email_id,
|
| 139 |
"is_verified": user.is_verified,
|
| 140 |
"dob": user.dob.isoformat() if user.dob else None,
|
| 141 |
+
"profile_picture": user.profile_picture,
|
| 142 |
+
"role": user_role.lower(),
|
| 143 |
},
|
| 144 |
"home_data": {
|
| 145 |
"announcements": ["Welcome!", "New protocol released"],
|
src/profile/router.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
from src.core.database import get_async_session
|
| 2 |
from src.auth.utils import get_current_user
|
| 3 |
from src.profile.models import Leave, LeaveType, LeaveStatus
|
|
@@ -76,8 +77,6 @@ async def mentor_decision_route(
|
|
| 76 |
detail="Comment is required when rejecting leave",
|
| 77 |
)
|
| 78 |
|
| 79 |
-
|
| 80 |
-
|
| 81 |
try:
|
| 82 |
leave = await mentor_decide_leave(session, mentor_uuid, leave_uuid, body)
|
| 83 |
except PermissionError as e:
|
|
@@ -212,33 +211,42 @@ async def list_notifications(
|
|
| 212 |
return {"code": 200, "data": notifications}
|
| 213 |
|
| 214 |
|
| 215 |
-
@router.get("/leave/{leave_id}")
|
| 216 |
async def get_leave_details(
|
| 217 |
leave_id: str,
|
| 218 |
session: AsyncSession = Depends(get_async_session),
|
| 219 |
user_id: str = Depends(get_current_user),
|
| 220 |
):
|
| 221 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
|
| 223 |
-
|
|
|
|
|
|
|
| 224 |
raise HTTPException(status_code=404, detail="Leave not found")
|
| 225 |
|
| 226 |
-
|
|
|
|
| 227 |
return {
|
| 228 |
"code": 200,
|
| 229 |
"data": {
|
| 230 |
"id": str(leave.id),
|
| 231 |
"user_id": str(leave.user_id),
|
|
|
|
| 232 |
"mentor_id": str(leave.mentor_id),
|
| 233 |
"lead_id": str(leave.lead_id),
|
| 234 |
"leave_type": leave.leave_type,
|
| 235 |
-
"from_date":
|
| 236 |
-
"to_date":
|
| 237 |
"days": leave.days,
|
| 238 |
"reason": leave.reason,
|
| 239 |
"status": leave.status,
|
| 240 |
"reject_reason": leave.reject_reason,
|
| 241 |
-
"updated_at": leave.updated_at.isoformat(),
|
| 242 |
},
|
| 243 |
}
|
| 244 |
|
|
@@ -248,32 +256,122 @@ async def mentor_pending_leaves(
|
|
| 248 |
session: AsyncSession = Depends(get_async_session),
|
| 249 |
mentor_id: str = Depends(get_current_user),
|
| 250 |
):
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
)
|
| 255 |
|
| 256 |
rows = (await session.exec(stmt)).all()
|
| 257 |
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
|
|
|
| 261 |
LeaveResponse(
|
| 262 |
-
id=str(
|
| 263 |
-
leave_type=
|
| 264 |
-
from_date=
|
| 265 |
-
to_date=
|
| 266 |
-
days=
|
| 267 |
-
reason=
|
| 268 |
-
status=
|
| 269 |
-
|
| 270 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
)
|
| 272 |
-
|
| 273 |
-
|
|
|
|
|
|
|
|
|
|
| 274 |
}
|
| 275 |
|
| 276 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
@router.get("/contacts", response_model=BaseResponse)
|
| 278 |
async def get_leave_contacts(
|
| 279 |
current_user=Depends(get_current_user),
|
|
|
|
| 1 |
+
from src.profile.schemas import LeaveDetailResponse
|
| 2 |
from src.core.database import get_async_session
|
| 3 |
from src.auth.utils import get_current_user
|
| 4 |
from src.profile.models import Leave, LeaveType, LeaveStatus
|
|
|
|
| 77 |
detail="Comment is required when rejecting leave",
|
| 78 |
)
|
| 79 |
|
|
|
|
|
|
|
| 80 |
try:
|
| 81 |
leave = await mentor_decide_leave(session, mentor_uuid, leave_uuid, body)
|
| 82 |
except PermissionError as e:
|
|
|
|
| 211 |
return {"code": 200, "data": notifications}
|
| 212 |
|
| 213 |
|
| 214 |
+
@router.get("/leave/{leave_id}", response_model=LeaveDetailResponse)
|
| 215 |
async def get_leave_details(
|
| 216 |
leave_id: str,
|
| 217 |
session: AsyncSession = Depends(get_async_session),
|
| 218 |
user_id: str = Depends(get_current_user),
|
| 219 |
):
|
| 220 |
+
# Join Leave + Users table to get user_name
|
| 221 |
+
stmt = (
|
| 222 |
+
select(Leave, Users.user_name)
|
| 223 |
+
.join(Users, Users.id == Leave.user_id)
|
| 224 |
+
.where(Leave.id == uuid.UUID(leave_id))
|
| 225 |
+
)
|
| 226 |
|
| 227 |
+
row = (await session.exec(stmt)).first()
|
| 228 |
+
|
| 229 |
+
if not row:
|
| 230 |
raise HTTPException(status_code=404, detail="Leave not found")
|
| 231 |
|
| 232 |
+
leave, user_name = row
|
| 233 |
+
|
| 234 |
return {
|
| 235 |
"code": 200,
|
| 236 |
"data": {
|
| 237 |
"id": str(leave.id),
|
| 238 |
"user_id": str(leave.user_id),
|
| 239 |
+
"user_name": user_name,
|
| 240 |
"mentor_id": str(leave.mentor_id),
|
| 241 |
"lead_id": str(leave.lead_id),
|
| 242 |
"leave_type": leave.leave_type,
|
| 243 |
+
"from_date": leave.from_date.isoformat(),
|
| 244 |
+
"to_date": leave.to_date.isoformat(),
|
| 245 |
"days": leave.days,
|
| 246 |
"reason": leave.reason,
|
| 247 |
"status": leave.status,
|
| 248 |
"reject_reason": leave.reject_reason,
|
| 249 |
+
"updated_at": leave.updated_at.isoformat() if leave.updated_at else None,
|
| 250 |
},
|
| 251 |
}
|
| 252 |
|
|
|
|
| 256 |
session: AsyncSession = Depends(get_async_session),
|
| 257 |
mentor_id: str = Depends(get_current_user),
|
| 258 |
):
|
| 259 |
+
print("🔥 mentor pending called for:", mentor_id)
|
| 260 |
+
|
| 261 |
+
stmt = (
|
| 262 |
+
select(Leave, Users.user_name)
|
| 263 |
+
.join(Users, Users.id == Leave.user_id)
|
| 264 |
+
.where(
|
| 265 |
+
Leave.mentor_id == uuid.UUID(mentor_id),
|
| 266 |
+
Leave.status == LeaveStatus.PENDING,
|
| 267 |
+
)
|
| 268 |
)
|
| 269 |
|
| 270 |
rows = (await session.exec(stmt)).all()
|
| 271 |
|
| 272 |
+
result = []
|
| 273 |
+
|
| 274 |
+
for leave, user_name in rows:
|
| 275 |
+
result.append(
|
| 276 |
LeaveResponse(
|
| 277 |
+
id=str(leave.id),
|
| 278 |
+
leave_type=leave.leave_type,
|
| 279 |
+
from_date=leave.from_date.isoformat(),
|
| 280 |
+
to_date=leave.to_date.isoformat(),
|
| 281 |
+
days=leave.days,
|
| 282 |
+
reason=leave.reason,
|
| 283 |
+
status=(
|
| 284 |
+
leave.status.value
|
| 285 |
+
if hasattr(leave.status, "value")
|
| 286 |
+
else leave.status
|
| 287 |
+
),
|
| 288 |
+
mentor_id=str(leave.mentor_id),
|
| 289 |
+
lead_id=str(leave.lead_id),
|
| 290 |
+
user_name=user_name,
|
| 291 |
+
updated_at=(
|
| 292 |
+
leave.updated_at.isoformat()
|
| 293 |
+
if hasattr(leave, "updated_at") and leave.updated_at
|
| 294 |
+
else None
|
| 295 |
+
),
|
| 296 |
)
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
return {
|
| 300 |
+
"code": 200,
|
| 301 |
+
"data": result,
|
| 302 |
}
|
| 303 |
|
| 304 |
|
| 305 |
+
@router.get("/my-leaves")
|
| 306 |
+
async def my_leave_history(
|
| 307 |
+
session: AsyncSession = Depends(get_async_session),
|
| 308 |
+
user_id: str = Depends(get_current_user),
|
| 309 |
+
):
|
| 310 |
+
stmt = (
|
| 311 |
+
select(Leave, Users.user_name, Users.email_id)
|
| 312 |
+
.join(Users, Users.id == Leave.mentor_id, isouter=True)
|
| 313 |
+
.where(Leave.user_id == uuid.UUID(user_id))
|
| 314 |
+
.order_by(desc(Leave.updated_at))
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
rows = (await session.exec(stmt)).all()
|
| 318 |
+
|
| 319 |
+
result = []
|
| 320 |
+
for leave, mentor_name, mentor_email in rows:
|
| 321 |
+
result.append(
|
| 322 |
+
{
|
| 323 |
+
"id": str(leave.id),
|
| 324 |
+
"leave_type": leave.leave_type,
|
| 325 |
+
"from_date": leave.from_date.isoformat(),
|
| 326 |
+
"to_date": leave.to_date.isoformat(),
|
| 327 |
+
"days": leave.days,
|
| 328 |
+
"reason": leave.reason,
|
| 329 |
+
"status": leave.status,
|
| 330 |
+
"mentor_name": mentor_name,
|
| 331 |
+
"updated_at": (
|
| 332 |
+
leave.updated_at.isoformat() if leave.updated_at else None
|
| 333 |
+
),
|
| 334 |
+
}
|
| 335 |
+
)
|
| 336 |
+
|
| 337 |
+
return {"code": 200, "data": result}
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
@router.get("/mentor/team-leaves")
|
| 341 |
+
async def team_leave_history(
|
| 342 |
+
session: AsyncSession = Depends(get_async_session),
|
| 343 |
+
mentor_id: str = Depends(get_current_user),
|
| 344 |
+
):
|
| 345 |
+
stmt = (
|
| 346 |
+
select(Leave, Users.user_name)
|
| 347 |
+
.join(Users, Users.id == Leave.user_id)
|
| 348 |
+
.where(Leave.mentor_id == uuid.UUID(mentor_id))
|
| 349 |
+
.order_by(desc(Leave.updated_at))
|
| 350 |
+
)
|
| 351 |
+
|
| 352 |
+
rows = (await session.exec(stmt)).all()
|
| 353 |
+
|
| 354 |
+
result = []
|
| 355 |
+
for leave, username in rows:
|
| 356 |
+
result.append(
|
| 357 |
+
{
|
| 358 |
+
"id": str(leave.id),
|
| 359 |
+
"user_name": username,
|
| 360 |
+
"leave_type": leave.leave_type,
|
| 361 |
+
"from_date": leave.from_date.isoformat(),
|
| 362 |
+
"to_date": leave.to_date.isoformat(),
|
| 363 |
+
"days": leave.days,
|
| 364 |
+
"reason": leave.reason,
|
| 365 |
+
"status": leave.status,
|
| 366 |
+
"updated_at": (
|
| 367 |
+
leave.updated_at.isoformat() if leave.updated_at else None
|
| 368 |
+
),
|
| 369 |
+
}
|
| 370 |
+
)
|
| 371 |
+
|
| 372 |
+
return {"code": 200, "data": result}
|
| 373 |
+
|
| 374 |
+
|
| 375 |
@router.get("/contacts", response_model=BaseResponse)
|
| 376 |
async def get_leave_contacts(
|
| 377 |
current_user=Depends(get_current_user),
|
src/profile/schemas.py
CHANGED
|
@@ -31,6 +31,27 @@ class ApproveRejectRequest(BaseModel):
|
|
| 31 |
comment: Optional[str] = None # optional for approve, required for reject
|
| 32 |
|
| 33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
class LeaveResponse(BaseModel):
|
| 35 |
id: str
|
| 36 |
leave_type: LeaveType
|
|
@@ -41,6 +62,8 @@ class LeaveResponse(BaseModel):
|
|
| 41 |
status: LeaveStatus
|
| 42 |
mentor_id: str
|
| 43 |
lead_id: str
|
|
|
|
|
|
|
| 44 |
|
| 45 |
|
| 46 |
class DeviceTokenIn(BaseModel):
|
|
|
|
| 31 |
comment: Optional[str] = None # optional for approve, required for reject
|
| 32 |
|
| 33 |
|
| 34 |
+
class LeaveDetail(BaseModel):
|
| 35 |
+
id: str
|
| 36 |
+
user_id: str
|
| 37 |
+
user_name: str # 👈 NEW FIELD
|
| 38 |
+
mentor_id: Optional[str]
|
| 39 |
+
lead_id: Optional[str]
|
| 40 |
+
leave_type: str
|
| 41 |
+
from_date: str
|
| 42 |
+
to_date: str
|
| 43 |
+
days: int
|
| 44 |
+
reason: str
|
| 45 |
+
status: str
|
| 46 |
+
reject_reason: Optional[str]
|
| 47 |
+
updated_at: Optional[str]
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class LeaveDetailResponse(BaseModel):
|
| 51 |
+
code: int
|
| 52 |
+
data: LeaveDetail
|
| 53 |
+
|
| 54 |
+
|
| 55 |
class LeaveResponse(BaseModel):
|
| 56 |
id: str
|
| 57 |
leave_type: LeaveType
|
|
|
|
| 62 |
status: LeaveStatus
|
| 63 |
mentor_id: str
|
| 64 |
lead_id: str
|
| 65 |
+
user_name: Optional[str] = None
|
| 66 |
+
updated_at: Optional[str] = None
|
| 67 |
|
| 68 |
|
| 69 |
class DeviceTokenIn(BaseModel):
|