Adjustments to the Devices Layout

This commit is contained in:
2026-02-18 10:22:41 +02:00
parent d6e522deb8
commit f54cdd525d
8 changed files with 918 additions and 335 deletions

View File

@@ -75,6 +75,7 @@ class DeviceAttributes(BaseModel):
networkSettings: DeviceNetworkSettings = DeviceNetworkSettings()
serialLogLevel: int = 0
sdLogLevel: int = 0
mqttLogLevel: int = 0
class DeviceSubInformation(BaseModel):
@@ -151,3 +152,16 @@ class DeviceInDB(DeviceCreate):
class DeviceListResponse(BaseModel):
devices: List[DeviceInDB]
total: int
class DeviceUserInfo(BaseModel):
"""User info resolved from device_users sub-collection or user_list."""
user_id: str = ""
display_name: str = ""
email: str = ""
role: str = ""
class DeviceUsersResponse(BaseModel):
users: List[DeviceUserInfo]
total: int

View File

@@ -4,6 +4,7 @@ from auth.models import TokenPayload
from auth.dependencies import require_device_access, require_viewer
from devices.models import (
DeviceCreate, DeviceUpdate, DeviceInDB, DeviceListResponse,
DeviceUsersResponse, DeviceUserInfo,
)
from devices import service
@@ -33,6 +34,16 @@ async def get_device(
return service.get_device(device_id)
@router.get("/{device_id}/users", response_model=DeviceUsersResponse)
async def get_device_users(
device_id: str,
_user: TokenPayload = Depends(require_viewer),
):
users_data = service.get_device_users(device_id)
users = [DeviceUserInfo(**u) for u in users_data]
return DeviceUsersResponse(users=users, total=len(users))
@router.post("", response_model=DeviceInDB, status_code=201)
async def create_device(
body: DeviceCreate,

View File

@@ -165,6 +165,94 @@ def update_device(device_doc_id: str, data: DeviceUpdate) -> DeviceInDB:
return _doc_to_device(updated_doc)
def get_device_users(device_doc_id: str) -> list[dict]:
"""Get users assigned to a device from the device_users sub-collection.
Falls back to the user_list field on the device document if the
sub-collection is empty.
"""
db = get_db()
doc_ref = db.collection(COLLECTION).document(device_doc_id)
doc = doc_ref.get()
if not doc.exists:
raise NotFoundError("Device")
# Try sub-collection first
sub_docs = list(doc_ref.collection("device_users").stream())
users = []
if sub_docs:
for sub_doc in sub_docs:
sub_data = sub_doc.to_dict()
role = sub_data.get("role", "")
user_ref = sub_data.get("user_reference")
# Resolve user reference
user_id = ""
display_name = ""
email = ""
if isinstance(user_ref, DocumentReference):
try:
user_doc = user_ref.get()
if user_doc.exists:
user_data = user_doc.to_dict()
user_id = user_doc.id
display_name = user_data.get("display_name", "")
email = user_data.get("email", "")
except Exception as e:
print(f"[devices] Error resolving user reference: {e}")
continue
elif isinstance(user_ref, str):
# String path like "users/abc123"
try:
ref_doc = db.document(user_ref).get()
if ref_doc.exists:
user_data = ref_doc.to_dict()
user_id = ref_doc.id
display_name = user_data.get("display_name", "")
email = user_data.get("email", "")
except Exception as e:
print(f"[devices] Error resolving user path: {e}")
continue
users.append({
"user_id": user_id,
"display_name": display_name,
"email": email,
"role": role,
})
else:
# Fallback to user_list field
device_data = doc.to_dict()
user_list = device_data.get("user_list", [])
for entry in user_list:
try:
if isinstance(entry, DocumentReference):
user_doc = entry.get()
elif isinstance(entry, str) and entry.strip():
# Could be a path like "users/abc" or a raw doc ID
if "/" in entry:
user_doc = db.document(entry).get()
else:
user_doc = db.collection("users").document(entry).get()
else:
continue
if user_doc.exists:
user_data = user_doc.to_dict()
users.append({
"user_id": user_doc.id,
"display_name": user_data.get("display_name", ""),
"email": user_data.get("email", ""),
"role": "",
})
except Exception as e:
print(f"[devices] Error resolving user_list entry: {e}")
return users
def delete_device(device_doc_id: str) -> None:
"""Delete a device document from Firestore."""
db = get_db()

View File

@@ -44,16 +44,19 @@ def _resolve_names(db, device_id: str | None, user_id: str | None) -> tuple[str,
device_name = ""
user_name = ""
if device_id:
device_doc = db.collection("devices").document(device_id).get()
if device_doc.exists:
device_name = device_doc.to_dict().get("device_name", "")
try:
if device_id and isinstance(device_id, str) and device_id.strip():
device_doc = db.collection("devices").document(device_id.strip()).get()
if device_doc.exists:
device_name = device_doc.to_dict().get("device_name", "")
if user_id:
user_doc = db.collection("users").document(user_id).get()
if user_doc.exists:
user_doc_data = user_doc.to_dict()
user_name = user_doc_data.get("display_name", "") or user_doc_data.get("email", "")
if user_id and isinstance(user_id, str) and user_id.strip():
user_doc = db.collection("users").document(user_id.strip()).get()
if user_doc.exists:
user_doc_data = user_doc.to_dict()
user_name = user_doc_data.get("display_name", "") or user_doc_data.get("email", "")
except Exception as e:
print(f"[equipment] Error resolving names (device_id={device_id}, user_id={user_id}): {e}")
return device_name, user_name