|
1 | 1 | from datetime import UTC, datetime |
| 2 | +from decimal import Decimal |
2 | 3 |
|
3 | 4 | from fastapi import APIRouter, Depends, HTTPException, Query |
4 | 5 | from passlib.context import CryptContext |
| 6 | +from sqlalchemy import func |
5 | 7 | from sqlalchemy.orm import Session |
6 | 8 |
|
7 | 9 | from app.auth.deps import get_current_device, require_admin_user, require_checkout_device, require_session_user |
|
10 | 12 | from app.database import get_db |
11 | 13 | from app.models.machine import Machine, MachineAuthorization |
12 | 14 | from app.models.rental import Rental |
| 15 | +from app.models.session import MachineSession |
13 | 16 | from app.models.transaction import Transaction |
14 | 17 | from app.models.user import User |
15 | 18 | from app.schemas.common import MessageResponse |
16 | 19 | from app.schemas.transaction import MeTransactionResponse |
17 | 20 | from app.schemas.user import ( |
18 | 21 | LinkTokenResponse, UserAuthResponse, UserCreate, UserLinkOidc, |
19 | | - UserMeMachineResponse, UserMeRentalResponse, UserResponse, UserUpdate, |
| 22 | + UserMeMachineResponse, UserMeRentalResponse, UserMeSessionResponse, |
| 23 | + UserResponse, UserUpdate, |
20 | 24 | ) |
21 | 25 |
|
22 | 26 | router = APIRouter() |
@@ -126,6 +130,51 @@ def get_me_machines( |
126 | 130 | ] |
127 | 131 |
|
128 | 132 |
|
| 133 | +@router.get("/me/sessions", response_model=list[UserMeSessionResponse]) |
| 134 | +def get_me_sessions( |
| 135 | + limit: int = Query(default=10, ge=1, le=100), |
| 136 | + offset: int = Query(default=0, ge=0), |
| 137 | + user: dict = Depends(require_session_user), |
| 138 | + db: Session = Depends(get_db), |
| 139 | +): |
| 140 | + """Machine session history for the logged-in user, newest first.""" |
| 141 | + db_user = _me_user(user, db) |
| 142 | + sessions = ( |
| 143 | + db.query(MachineSession) |
| 144 | + .filter(MachineSession.user_id == db_user.id) |
| 145 | + .order_by(MachineSession.start_time.desc()) |
| 146 | + .offset(offset) |
| 147 | + .limit(limit) |
| 148 | + .all() |
| 149 | + ) |
| 150 | + |
| 151 | + costs: dict[int, Decimal] = {} |
| 152 | + session_ids = [s.id for s in sessions] |
| 153 | + if session_ids: |
| 154 | + for sid, total in ( |
| 155 | + db.query(Transaction.session_id, func.sum(Transaction.amount)) |
| 156 | + .filter(Transaction.session_id.in_(session_ids)) |
| 157 | + .group_by(Transaction.session_id) |
| 158 | + .all() |
| 159 | + ): |
| 160 | + costs[sid] = Decimal(str(total or 0)) |
| 161 | + |
| 162 | + result = [] |
| 163 | + for s in sessions: |
| 164 | + duration = int((s.end_time - s.start_time).total_seconds()) if s.end_time else None |
| 165 | + cost = max(-costs.get(s.id, Decimal("0.00")), Decimal("0.00")) |
| 166 | + result.append(UserMeSessionResponse( |
| 167 | + id=s.id, |
| 168 | + machine_name=s.machine.name, |
| 169 | + machine_slug=s.machine.slug, |
| 170 | + start_time=s.start_time, |
| 171 | + end_time=s.end_time, |
| 172 | + duration_seconds=duration, |
| 173 | + total_cost=cost, |
| 174 | + )) |
| 175 | + return result |
| 176 | + |
| 177 | + |
129 | 178 | # --------------------------------------------------------------------------- |
130 | 179 | # Device endpoints |
131 | 180 | # --------------------------------------------------------------------------- |
|
0 commit comments