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