Sprint C#15-16: paginate Approve Identified API/UI and in-app photo preview.

This commit is contained in:
2026-08-04 22:03:35 -04:00
parent a2a3c7e9c2
commit 6d9e270ac2
3 changed files with 127 additions and 53 deletions
+43 -46
View File
@@ -152,23 +152,37 @@ def list_pending_identifications(
dict, Depends(require_feature_permission("user_identified"))
],
include_denied: bool = False,
page: int = Query(1, ge=1, description="Page number (1-based)"),
page_size: int = Query(50, ge=1, le=500, description="Items per page"),
db: Session = Depends(get_auth_db),
main_db: Session = Depends(get_db),
) -> PendingIdentificationsListResponse:
"""List all pending identifications from the auth database.
This endpoint reads from the separate auth database (DATABASE_URL_AUTH)
and returns all pending identifications from the pending_identifications table.
"""List pending identifications from the auth database (paginated).
By default, only shows records with status='pending' for approval.
Set include_denied=True to also show denied records.
"""
try:
# Query pending_identifications from auth database using raw SQL
# Join with users table to get user name/email
# Filter by status='pending' to show only records awaiting approval
# Optionally include denied records if include_denied is True
if include_denied:
result = db.execute(text("""
where_clause = (
"WHERE pi.status IN ('pending', 'denied')"
if include_denied
else "WHERE pi.status = 'pending'"
)
order_clause = (
"ORDER BY pi.status ASC, pi.last_name ASC, pi.first_name ASC, pi.created_at DESC"
if include_denied
else "ORDER BY pi.last_name ASC, pi.first_name ASC, pi.created_at DESC"
)
offset = (page - 1) * page_size
count_row = db.execute(
text(f"SELECT COUNT(*) AS count FROM pending_identifications pi {where_clause}")
).fetchone()
total = int(count_row.count) if count_row else 0
result = db.execute(
text(
f"""
SELECT
pi.id,
pi.face_id,
@@ -187,46 +201,29 @@ def list_pending_identifications(
pi.updated_at
FROM pending_identifications pi
LEFT JOIN users u ON pi.user_id = u.id
WHERE pi.status IN ('pending', 'denied')
ORDER BY pi.status ASC, pi.last_name ASC, pi.first_name ASC, pi.created_at DESC
"""))
else:
result = db.execute(text("""
SELECT
pi.id,
pi.face_id,
pi.user_id,
u.name as user_name,
u.email as user_email,
pi.first_name,
pi.last_name,
pi.middle_name,
pi.maiden_name,
pi.date_of_birth,
pi.email,
pi.phone,
pi.status,
pi.created_at,
pi.updated_at
FROM pending_identifications pi
LEFT JOIN users u ON pi.user_id = u.id
WHERE pi.status = 'pending'
ORDER BY pi.last_name ASC, pi.first_name ASC, pi.created_at DESC
"""))
{where_clause}
{order_clause}
LIMIT :limit OFFSET :offset
"""
),
{"limit": page_size, "offset": offset},
)
rows = result.fetchall()
face_ids = [row.face_id for row in rows]
photo_by_face: dict[int, int | None] = {}
if face_ids:
for face_id, photo_id in main_db.query(Face.id, Face.photo_id).filter(
Face.id.in_(face_ids)
):
photo_by_face[face_id] = photo_id
items = []
for row in rows:
# Get photo_id from main database
photo_id = None
face = main_db.query(Face).filter(Face.id == row.face_id).first()
if face:
photo_id = face.photo_id
items.append(PendingIdentificationResponse(
id=row.id,
face_id=row.face_id,
photo_id=photo_id,
photo_id=photo_by_face.get(row.face_id),
user_id=row.user_id,
user_name=row.user_name,
user_email=row.user_email,
@@ -241,8 +238,8 @@ def list_pending_identifications(
created_at=str(row.created_at) if row.created_at else '',
updated_at=str(row.updated_at) if row.updated_at else '',
))
return PendingIdentificationsListResponse(items=items, total=len(items))
return PendingIdentificationsListResponse(items=items, total=total)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,