"""Audit log query service — framework-agnostic query logic for audit logs. Provides paginated logs and distinct action/entity-type lists. No FastAPI imports. """ # Enable future language features for compatibility from __future__ import annotations # Import datetime from datetime from datetime import datetime # Import Session, joinedload from sqlalchemy.orm from sqlalchemy.orm import Session, joinedload # Import AuditLog from app.models.audit from app.models.audit import AuditLog # Define function list_logs def list_logs( # Entry: db db: Session, *, # Entry: user_id user_id: str | None = None, # Entry: action action: str | None = None, # Entry: entity_type entity_type: str | None = None, # Entry: start_date start_date: datetime | None = None, # Entry: end_date end_date: datetime | None = None, # Entry: offset offset: int = 0, # Entry: limit limit: int = 50, ) -> dict: """Return paginated audit logs with optional filters. Returns a dict with keys: items, total, offset, limit. Each item is a dict with: id, user_id, username, action, entity_type, entity_id, timestamp, details. """ # Assign query = db.query(AuditLog).options(joinedload(AuditLog.user)) query = db.query(AuditLog).options(joinedload(AuditLog.user)) # Check: user_id if user_id: # Assign query = query.filter(AuditLog.user_id == user_id) query = query.filter(AuditLog.user_id == user_id) # Check: action if action: # Assign query = query.filter(AuditLog.action == action) query = query.filter(AuditLog.action == action) # Check: entity_type if entity_type: # Assign query = query.filter(AuditLog.entity_type == entity_type) query = query.filter(AuditLog.entity_type == entity_type) # Check: start_date if start_date: # Assign query = query.filter(AuditLog.timestamp >= start_date) query = query.filter(AuditLog.timestamp >= start_date) # Check: end_date if end_date: # Assign query = query.filter(AuditLog.timestamp <= end_date) query = query.filter(AuditLog.timestamp <= end_date) # Assign total = query.count() total = query.count() # Assign logs = ( logs = ( query # Chain .order_by() call .order_by(AuditLog.timestamp.desc()) # Chain .offset() call .offset(offset) # Chain .limit() call .limit(limit) # Chain .all() call .all() ) # Assign items = [ items = [ { # Literal argument value "id": log.id, # Literal argument value "user_id": log.user_id, # Literal argument value "username": log.user.username if log.user else None, # Literal argument value "action": log.action, # Literal argument value "entity_type": log.entity_type, # Literal argument value "entity_id": log.entity_id, # Literal argument value "timestamp": log.timestamp, # Literal argument value "details": log.details, } for log in logs ] # Return {"items": items, "total": total, "offset": offset, "limit": limit} return {"items": items, "total": total, "offset": offset, "limit": limit} # Define function list_distinct_actions def list_distinct_actions(db: Session) -> list[str]: """Return a list of distinct action types in the audit log.""" # Assign actions = ( actions = ( db.query(AuditLog.action) # Chain .distinct() call .distinct() # Chain .order_by() call .order_by(AuditLog.action) # Chain .all() call .all() ) # Return [a[0] for a in actions] return [a[0] for a in actions] # Define function list_distinct_entity_types def list_distinct_entity_types(db: Session) -> list[str]: """Return a list of distinct entity types in the audit log.""" # Assign types = ( types = ( db.query(AuditLog.entity_type) # Chain .filter() call .filter(AuditLog.entity_type.isnot(None)) # Chain .distinct() call .distinct() # Chain .order_by() call .order_by(AuditLog.entity_type) # Chain .all() call .all() ) # Return [t[0] for t in types] return [t[0] for t in types]