Spaces:
Runtime error
Runtime error
Fixed issue push
Browse files- src/crud/__init__.py +11 -4
- src/crud/clearance.py +25 -0
- src/crud/tag_linking.py +9 -6
- src/routers/token.py +5 -2
- src/routers/users.py +14 -4
src/crud/__init__.py
CHANGED
|
@@ -23,6 +23,7 @@ from .users import (
|
|
| 23 |
get_user_by_id,
|
| 24 |
delete_user,
|
| 25 |
hash_password, # Import hash_password from users module
|
|
|
|
| 26 |
)
|
| 27 |
from .devices import (
|
| 28 |
get_device_by_id_str,
|
|
@@ -35,11 +36,14 @@ from .clearance import (
|
|
| 35 |
get_clearance_statuses_by_student_id,
|
| 36 |
update_clearance_status,
|
| 37 |
delete_clearance_status,
|
|
|
|
|
|
|
| 38 |
)
|
| 39 |
from .tag_linking import (
|
| 40 |
create_pending_tag_link,
|
| 41 |
-
|
| 42 |
-
|
|
|
|
| 43 |
)
|
| 44 |
|
| 45 |
# Export all functions
|
|
@@ -52,6 +56,7 @@ __all__ = [
|
|
| 52 |
'get_user_by_id',
|
| 53 |
'delete_user',
|
| 54 |
'hash_password',
|
|
|
|
| 55 |
# Students
|
| 56 |
'create_student',
|
| 57 |
'get_all_students',
|
|
@@ -71,6 +76,8 @@ __all__ = [
|
|
| 71 |
'delete_clearance_status',
|
| 72 |
# Tag Linking
|
| 73 |
'create_pending_tag_link',
|
| 74 |
-
'
|
| 75 |
-
'
|
|
|
|
|
|
|
| 76 |
]
|
|
|
|
| 23 |
get_user_by_id,
|
| 24 |
delete_user,
|
| 25 |
hash_password, # Import hash_password from users module
|
| 26 |
+
get_all_users
|
| 27 |
)
|
| 28 |
from .devices import (
|
| 29 |
get_device_by_id_str,
|
|
|
|
| 36 |
get_clearance_statuses_by_student_id,
|
| 37 |
update_clearance_status,
|
| 38 |
delete_clearance_status,
|
| 39 |
+
get_all_clearance_status,
|
| 40 |
+
get_student_clearance_status
|
| 41 |
)
|
| 42 |
from .tag_linking import (
|
| 43 |
create_pending_tag_link,
|
| 44 |
+
get_pending_link_by_id,
|
| 45 |
+
delete_pending_link_by_device_id,
|
| 46 |
+
get_pending_links,
|
| 47 |
)
|
| 48 |
|
| 49 |
# Export all functions
|
|
|
|
| 56 |
'get_user_by_id',
|
| 57 |
'delete_user',
|
| 58 |
'hash_password',
|
| 59 |
+
'get_all_users',
|
| 60 |
# Students
|
| 61 |
'create_student',
|
| 62 |
'get_all_students',
|
|
|
|
| 76 |
'delete_clearance_status',
|
| 77 |
# Tag Linking
|
| 78 |
'create_pending_tag_link',
|
| 79 |
+
'get_pending_link_by_device_id',
|
| 80 |
+
'get_pending_link_by_token',
|
| 81 |
+
'delete_pending_link_by_id',
|
| 82 |
+
'get_all_pending_links',
|
| 83 |
]
|
src/crud/clearance.py
CHANGED
|
@@ -75,3 +75,28 @@ def delete_clearance_status(
|
|
| 75 |
db.commit()
|
| 76 |
|
| 77 |
return status_to_delete
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
db.commit()
|
| 76 |
|
| 77 |
return status_to_delete
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def get_all_clearance_status(db: Session) -> list[models.ClearanceStatus]:
|
| 81 |
+
"""
|
| 82 |
+
Retrieves all clearance statuses from the database.
|
| 83 |
+
|
| 84 |
+
This function is useful for administrative purposes, allowing staff to view
|
| 85 |
+
all clearance records across all students and departments.
|
| 86 |
+
"""
|
| 87 |
+
return db.query(models.ClearanceStatus).all()
|
| 88 |
+
|
| 89 |
+
def get_student_clearance_status(
|
| 90 |
+
db: Session,
|
| 91 |
+
student_id: str,
|
| 92 |
+
department: models.ClearanceDepartment
|
| 93 |
+
) -> models.ClearanceStatus | None:
|
| 94 |
+
"""
|
| 95 |
+
Retrieves the clearance status for a specific student in a specific department.
|
| 96 |
+
|
| 97 |
+
Returns None if no status exists for that student and department.
|
| 98 |
+
"""
|
| 99 |
+
return db.query(models.ClearanceStatus).filter(
|
| 100 |
+
models.ClearanceStatus.student_id == student_id,
|
| 101 |
+
models.ClearanceStatus.department == department
|
| 102 |
+
).first()
|
src/crud/tag_linking.py
CHANGED
|
@@ -26,16 +26,19 @@ def create_pending_tag_link(db: Session, link_details: models.PrepareTagLinkRequ
|
|
| 26 |
db.refresh(new_link)
|
| 27 |
return new_link
|
| 28 |
|
| 29 |
-
def
|
| 30 |
"""
|
| 31 |
Fetches the active (non-expired) pending tag link for a specific device.
|
| 32 |
"""
|
| 33 |
-
return db.query(models.PendingTagLink).
|
| 34 |
-
models.PendingTagLink.device_id_fk == device_id,
|
| 35 |
-
models.PendingTagLink.expires_at > datetime.utcnow()
|
| 36 |
-
).first()
|
| 37 |
|
| 38 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
"""Deletes a pending link, typically after it has been used."""
|
| 40 |
link_to_delete = db.query(models.PendingTagLink).filter(models.PendingTagLink.id == link_id).first()
|
| 41 |
if link_to_delete:
|
|
|
|
| 26 |
db.refresh(new_link)
|
| 27 |
return new_link
|
| 28 |
|
| 29 |
+
def get_pending_links(db: Session, device_id: int) -> models.PendingTagLink | None:
|
| 30 |
"""
|
| 31 |
Fetches the active (non-expired) pending tag link for a specific device.
|
| 32 |
"""
|
| 33 |
+
return db.query(models.PendingTagLink).offset(0).limit(limit=1000).all()
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
+
def get_pending_link_by_id(db: Session, link_id: int) -> models.PendingTagLink | None:
|
| 36 |
+
"""
|
| 37 |
+
Fetches a pending tag link by its ID.
|
| 38 |
+
"""
|
| 39 |
+
return db.query(models.PendingTagLink).filter(models.PendingTagLink.id == link_id).first()
|
| 40 |
+
|
| 41 |
+
def delete_pending_link_by_device_id(db: Session, link_id: int):
|
| 42 |
"""Deletes a pending link, typically after it has been used."""
|
| 43 |
link_to_delete = db.query(models.PendingTagLink).filter(models.PendingTagLink.id == link_id).first()
|
| 44 |
if link_to_delete:
|
src/routers/token.py
CHANGED
|
@@ -3,6 +3,7 @@ Router for handling user authentication and issuing JWT tokens.
|
|
| 3 |
"""
|
| 4 |
from fastapi import APIRouter, Depends, HTTPException, status
|
| 5 |
from fastapi.security import OAuth2PasswordRequestForm
|
|
|
|
| 6 |
from sqlalchemy.orm import Session
|
| 7 |
from datetime import timedelta
|
| 8 |
|
|
@@ -13,7 +14,7 @@ from src.config import settings
|
|
| 13 |
|
| 14 |
router = APIRouter(
|
| 15 |
prefix="/api",
|
| 16 |
-
tags=["Authentication"]
|
| 17 |
)
|
| 18 |
|
| 19 |
@router.post("/token", response_model=models.Token)
|
|
@@ -27,7 +28,9 @@ async def login_for_access_token(
|
|
| 27 |
This is the primary login endpoint. It takes a username and password
|
| 28 |
and returns an access token if the credentials are valid.
|
| 29 |
"""
|
| 30 |
-
user = await
|
|
|
|
|
|
|
| 31 |
if not user:
|
| 32 |
raise HTTPException(
|
| 33 |
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
| 3 |
"""
|
| 4 |
from fastapi import APIRouter, Depends, HTTPException, status
|
| 5 |
from fastapi.security import OAuth2PasswordRequestForm
|
| 6 |
+
from fastapi.concurrency import run_in_threadpool
|
| 7 |
from sqlalchemy.orm import Session
|
| 8 |
from datetime import timedelta
|
| 9 |
|
|
|
|
| 14 |
|
| 15 |
router = APIRouter(
|
| 16 |
prefix="/api",
|
| 17 |
+
tags=["Authentication Token"]
|
| 18 |
)
|
| 19 |
|
| 20 |
@router.post("/token", response_model=models.Token)
|
|
|
|
| 28 |
This is the primary login endpoint. It takes a username and password
|
| 29 |
and returns an access token if the credentials are valid.
|
| 30 |
"""
|
| 31 |
+
user = await run_in_threadpool(
|
| 32 |
+
authenticate_user, db, form_data.username, form_data.password
|
| 33 |
+
)
|
| 34 |
if not user:
|
| 35 |
raise HTTPException(
|
| 36 |
status_code=status.HTTP_401_UNAUTHORIZED,
|
src/routers/users.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
Router for managing users (staff, admins).
|
| 3 |
"""
|
| 4 |
from fastapi import APIRouter, Depends, HTTPException, status
|
|
|
|
| 5 |
from sqlalchemy.orm import Session
|
| 6 |
from typing import List
|
| 7 |
|
|
@@ -19,10 +20,10 @@ async def create_new_user(user: models.UserCreate, db: Session = Depends(get_db)
|
|
| 19 |
"""
|
| 20 |
Admin: Create a new user (staff or admin).
|
| 21 |
"""
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
|
| 27 |
@router.get("/", response_model=List[models.UserResponse], dependencies=[Depends(get_current_active_admin_user_from_token)])
|
| 28 |
async def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
|
@@ -32,9 +33,18 @@ async def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_
|
|
| 32 |
users = await crud.get_all_users(db, skip=skip, limit=limit) # Assumes get_all_users exists in crud.users
|
| 33 |
return users
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
@router.get("/me", response_model=models.UserResponse)
|
| 36 |
async def read_users_me(current_user: models.User = Depends(get_current_active_user)):
|
| 37 |
"""
|
| 38 |
Get profile information for the currently authenticated user.
|
| 39 |
"""
|
| 40 |
return current_user
|
|
|
|
|
|
| 2 |
Router for managing users (staff, admins).
|
| 3 |
"""
|
| 4 |
from fastapi import APIRouter, Depends, HTTPException, status
|
| 5 |
+
from fastapi.concurrency import run_in_threadpool
|
| 6 |
from sqlalchemy.orm import Session
|
| 7 |
from typing import List
|
| 8 |
|
|
|
|
| 20 |
"""
|
| 21 |
Admin: Create a new user (staff or admin).
|
| 22 |
"""
|
| 23 |
+
db_user = await run_in_threadpool(crud.get_user_by_username, db, user.username)
|
| 24 |
+
if db_user:
|
| 25 |
+
raise HTTPException(status_code=400, detail="Username already registered")
|
| 26 |
+
return await run_in_threadpool(crud.create_user, db, user)
|
| 27 |
|
| 28 |
@router.get("/", response_model=List[models.UserResponse], dependencies=[Depends(get_current_active_admin_user_from_token)])
|
| 29 |
async def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
|
|
|
| 33 |
users = await crud.get_all_users(db, skip=skip, limit=limit) # Assumes get_all_users exists in crud.users
|
| 34 |
return users
|
| 35 |
|
| 36 |
+
@router.get("/all", response_model=list[models.UserResponse], dependencies=[Depends(get_current_active_admin_user_from_token)])
|
| 37 |
+
async def get_all_users(db: Session = Depends(get_db)):
|
| 38 |
+
"""
|
| 39 |
+
Admin: Get a list of all users.
|
| 40 |
+
"""
|
| 41 |
+
users = await run_in_threadpool(crud.get_all_users, db)
|
| 42 |
+
return users
|
| 43 |
+
|
| 44 |
@router.get("/me", response_model=models.UserResponse)
|
| 45 |
async def read_users_me(current_user: models.User = Depends(get_current_active_user)):
|
| 46 |
"""
|
| 47 |
Get profile information for the currently authenticated user.
|
| 48 |
"""
|
| 49 |
return current_user
|
| 50 |
+
|