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 |
+
|