Skip to content

Commit

Permalink
feat(users): add pagination and search functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Anuj-Gupta4 committed Jan 29, 2025
1 parent 7e3f4de commit 82a309d
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 13 deletions.
35 changes: 27 additions & 8 deletions src/backend/app/db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,18 +218,37 @@ async def one(cls, db: Connection, user_identifier: int | str) -> Self:

@classmethod
async def all(
cls, db: Connection, skip: int = 0, limit: int = 100
cls,
db: Connection,
skip: Optional[int] = None,
limit: Optional[int] = None,
search: Optional[str] = None,
) -> Optional[list[Self]]:
"""Fetch all users."""
filters = []
params = {"offset": skip, "limit": limit} if skip and limit else {}

if search:
filters.append("username ILIKE %(search)s")
params["search"] = f"%{search}%"

sql = f"""
SELECT * FROM users
{"WHERE " + " AND ".join(filters) if filters else ""}
ORDER BY registered_at DESC
"""
sql += (
"""
OFFSET %(offset)s
LIMIT %(limit)s;
"""
if skip and limit
else ";"
)
async with db.cursor(row_factory=class_row(cls)) as cur:
await cur.execute(
"""
SELECT * FROM users
ORDER BY registered_at DESC
OFFSET %(offset)s
LIMIT %(limit)s;
""",
{"offset": skip, "limit": limit},
sql,
params,
)
return await cur.fetchall()

Expand Down
22 changes: 22 additions & 0 deletions src/backend/app/users/user_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from datetime import datetime, timedelta, timezone
from textwrap import dedent
from typing import Optional

from fastapi import Request
from loguru import logger as log
Expand All @@ -28,6 +29,7 @@

from app.auth.providers.osm import get_osm_token, send_osm_message
from app.db.models import DbUser
from app.projects.project_crud import get_pagination

WARNING_INTERVALS = [21, 14, 7] # Days before deletion
INACTIVITY_THRESHOLD = 2 * 365 # 2 years approx
Expand Down Expand Up @@ -115,3 +117,23 @@ async def send_warning_email_or_osm(
body=message_content,
)
log.info(f"Sent warning to {username}: {days_remaining} days remaining.")


async def get_paginated_users(
db: Connection,
page: int,
results_per_page: int,
search: Optional[str] = None,
) -> dict:
"""Helper function to fetch paginated users with optional filters."""
# Get subset of users
users = await DbUser.all(db, search=search)
start_index = (page - 1) * results_per_page
end_index = start_index + results_per_page
paginated_users = users[start_index:end_index]

pagination = await get_pagination(
page, len(paginated_users), results_per_page, len(users)
)

return {"results": paginated_users, "pagination": pagination}
13 changes: 8 additions & 5 deletions src/backend/app/users/user_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
#
"""Endpoints for users and role."""

from typing import Annotated, List
from typing import Annotated

from fastapi import APIRouter, Depends, Request, Response
from fastapi import APIRouter, Depends, Query, Request, Response
from loguru import logger as log
from psycopg import Connection

Expand All @@ -30,7 +30,7 @@
from app.db.enums import UserRole as UserRoleEnum
from app.db.models import DbUser
from app.users import user_schemas
from app.users.user_crud import process_inactive_users
from app.users.user_crud import get_paginated_users, process_inactive_users
from app.users.user_deps import get_user

router = APIRouter(
Expand All @@ -40,13 +40,16 @@
)


@router.get("", response_model=List[user_schemas.UserOut])
@router.get("", response_model=user_schemas.PaginatedUsers)
async def get_users(
db: Annotated[Connection, Depends(db_conn)],
current_user: Annotated[DbUser, Depends(super_admin)],
page: int = Query(1, ge=1),
results_per_page: int = Query(13, le=100),
search: str = None,
):
"""Get all user details."""
return await DbUser.all(db)
return await get_paginated_users(db, page, results_per_page, search)


@router.get("/user-role-options")
Expand Down
8 changes: 8 additions & 0 deletions src/backend/app/users/user_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

from app.db.enums import ProjectRole, UserRole
from app.db.models import DbUser, DbUserRole
from app.projects.project_schemas import PaginationInfo


class UserIn(DbUser):
Expand Down Expand Up @@ -71,3 +72,10 @@ class UserRolesOut(DbUserRole):

# project_id is redundant if the user specified it in the endpoint
project_id: Annotated[Optional[int], Field(exclude=True)] = None


class PaginatedUsers(BaseModel):
"""Project summaries + Pagination info."""

results: list[UserOut]
pagination: PaginationInfo

0 comments on commit 82a309d

Please sign in to comment.