You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

91 lines
2.5 KiB

from __future__ import annotations
from collections.abc import Sequence
from datetime import datetime, timedelta
from sqlalchemy import Select
from sqlalchemy.orm import selectinload
from app.extensions import db
from app.models import AuditLog
def audit_log_query(
*,
actor_user_id: int | None = None,
action_type: str | None = None,
from_date: str | None = None,
to_date: str | None = None,
) -> Select[tuple[AuditLog]]:
query = db.select(AuditLog).options(selectinload(AuditLog.actor))
if actor_user_id is not None:
query = query.where(AuditLog.actor_user_id == actor_user_id)
normalized_action_type = (action_type or "").strip()
if normalized_action_type:
query = query.where(AuditLog.action_type == normalized_action_type)
parsed_from_date = _parse_date_start(from_date)
if parsed_from_date is not None:
query = query.where(AuditLog.created_at >= parsed_from_date)
parsed_to_date = _parse_date_end(to_date)
if parsed_to_date is not None:
query = query.where(AuditLog.created_at < parsed_to_date)
return query
def list_audit_logs(
*,
actor_user_id: int | None = None,
action_type: str | None = None,
from_date: str | None = None,
to_date: str | None = None,
limit: int = 100,
) -> Sequence[AuditLog]:
result = db.session.execute(
audit_log_query(
actor_user_id=actor_user_id,
action_type=action_type,
from_date=from_date,
to_date=to_date,
)
.order_by(AuditLog.created_at.desc(), AuditLog.id.desc())
.limit(limit),
)
return result.scalars().all()
def get_audit_log_or_none(log_id: int) -> AuditLog | None:
result = db.session.execute(
db.select(AuditLog).options(selectinload(AuditLog.actor)).where(AuditLog.id == log_id),
)
return result.scalar_one_or_none()
def list_action_types() -> list[str]:
result = db.session.execute(
db.select(AuditLog.action_type).distinct().order_by(AuditLog.action_type.asc()),
)
return [value for value in result.scalars().all() if value]
def _parse_date_start(raw_value: str | None) -> datetime | None:
value = (raw_value or "").strip()
if not value:
return None
try:
return datetime.fromisoformat(value)
except ValueError:
return None
def _parse_date_end(raw_value: str | None) -> datetime | None:
start = _parse_date_start(raw_value)
if start is None:
return None
return start + timedelta(days=1)