config for logs and filters

This commit is contained in:
Mateusz Gruszczyński
2026-05-20 10:31:07 +02:00
parent 0a82211e4c
commit 4cff530b0e
11 changed files with 53 additions and 10 deletions

View File

@@ -22,7 +22,7 @@ from flask import Blueprint, jsonify, request, abort, send_file, redirect, Respo
from ..config import DB_PATH, JOBS_RETENTION_DAYS, SMART_QUEUE_HISTORY_RETENTION_DAYS, LOG_RETENTION_DAYS, WORKERS, PYTORRENT_TMP_DIR
from ..db import connect, utcnow
from ..services.auth import current_user_id as default_user_id, current_user, list_users, save_user, delete_user, login_user, logout_user, enabled as auth_enabled, require_profile_write
from ..services import preferences, rtorrent, torrent_stats, speed_peaks, tracker_cache, rss as rss_service, ratio_rules, backup as backup_service, download_planner
from ..services import preferences, rtorrent, torrent_stats, speed_peaks, tracker_cache, rss as rss_service, ratio_rules, backup as backup_service, download_planner, operation_logs
from ..services.torrent_cache import torrent_cache
from ..services.torrent_summary import cached_summary
from ..services.workers import enqueue, list_jobs, cancel_job, retry_job, force_job, clear_jobs, emergency_clear_jobs
@@ -288,6 +288,7 @@ def cleanup_summary() -> dict:
"WHERE profile_id=? OR profile_id IS NULL",
(profile_id,),
) if profile_id else _table_count("operation_logs")
operation_log_retention = operation_logs.get_settings(profile_id) if profile_id else operation_logs.get_settings(0)
return {
"jobs_total": _table_count("jobs"),
"jobs_clearable": _table_count("jobs", "WHERE status NOT IN ('pending', 'running')"),
@@ -299,10 +300,14 @@ def cleanup_summary() -> dict:
"retention_days": {
"jobs": JOBS_RETENTION_DAYS,
"smart_queue_history": SMART_QUEUE_HISTORY_RETENTION_DAYS,
"operation_logs": LOG_RETENTION_DAYS,
"operation_logs": operation_log_retention.get("retention_days", LOG_RETENTION_DAYS),
"automation_history": SMART_QUEUE_HISTORY_RETENTION_DAYS,
"planner_history": SMART_QUEUE_HISTORY_RETENTION_DAYS,
},
"operation_log_retention": operation_log_retention,
"retention_labels": {
"operation_logs": operation_logs.retention_label(operation_log_retention),
},
"database": _db_size(),
}

View File

@@ -23,6 +23,7 @@ def operation_logs_list():
offset=int(request.args.get("offset") or 0),
event_type=str(request.args.get("type") or "").strip(),
q=str(request.args.get("q") or "").strip(),
hide_jobs=str(request.args.get("hide_jobs") or "").lower() in {"1", "true", "yes", "on"},
)
data["stats"] = operation_logs.stats(int(profile["id"]))
data["settings"] = data["stats"].get("settings")

View File

@@ -125,7 +125,7 @@ def record_cache_diff(profile_id: int, added: list[dict], removed: list[str], up
record(profile_id, "torrent_completed", f"Torrent completed: {old.get('name') or h}", source="poller", torrent_hash=h, torrent_name=old.get("name"), details={"ratio": patch.get("ratio", old.get("ratio")), "size": old.get("size"), "path": old.get("path")})
def list_logs(profile_id: int, *, limit: int = 200, offset: int = 0, event_type: str = "", q: str = "") -> dict:
def list_logs(profile_id: int, *, limit: int = 200, offset: int = 0, event_type: str = "", q: str = "", hide_jobs: bool = False) -> dict:
limit = max(1, min(int(limit or 200), 1000))
offset = max(0, int(offset or 0))
where = ["(profile_id=? OR profile_id IS NULL)"]
@@ -133,6 +133,9 @@ def list_logs(profile_id: int, *, limit: int = 200, offset: int = 0, event_type:
if event_type:
where.append("event_type=?")
params.append(event_type)
if hide_jobs:
# Note: Job-originated rows include torrent_added/torrent_removed events, so source is the reliable filter.
where.append("COALESCE(source, '') <> 'job'")
if q:
where.append("(message LIKE ? OR torrent_name LIKE ? OR torrent_hash LIKE ? OR action LIKE ?)")
like = f"%{q}%"
@@ -155,6 +158,17 @@ def stats(profile_id: int) -> dict:
return {"total": int(total or 0), "by_type": by_type, "by_day": by_day, "by_month": by_month, "top_actions": top_actions, "settings": get_settings(profile_id)}
def retention_label(settings: dict) -> str:
mode = settings.get("retention_mode") or "days"
if mode == "manual":
return "manual cleanup only"
if mode == "lines":
return f"retention {settings.get('retention_lines') or DEFAULT_SETTINGS['retention_lines']} lines"
if mode == "both":
return f"retention {settings.get('retention_days') or DEFAULT_SETTINGS['retention_days']} days and {settings.get('retention_lines') or DEFAULT_SETTINGS['retention_lines']} lines"
return f"retention {settings.get('retention_days') or DEFAULT_SETTINGS['retention_days']} days"
def clear(profile_id: int, *, event_type: str = "") -> int:
where = ["(profile_id=? OR profile_id IS NULL)"]
params: list[Any] = [int(profile_id or 0)]

View File

@@ -38,6 +38,9 @@ RECOMMENDED_TABLE_COLUMNS = {
"created": False, "priority": False, "state": False, "active": False, "complete": False,
"hashing": False, "message": False, "hash": False,
},
"mobileSortFilters": {
"seeds:-1": True, "up_rate:-1": True, "down_rate:-1": True, "progress:-1": True,
},
"mobileSmartFiltersEnabled": False,
"widths": {
"select": 44, "name": 389, "status": 83, "size": 75, "progress": 177,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3338,6 +3338,21 @@ body.mobile-mode .mobile-filter-bar {
grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
gap: 0.55rem;
}
.column-config-section {
display: grid;
gap: 0.5rem;
margin-bottom: 1rem;
}
.column-config-section:last-child {
margin-bottom: 0;
}
.column-config-section h6 {
margin: 0;
}
.mobile-progress:empty {
display: none;
}
@@ -4181,6 +4196,11 @@ body,
max-width: 260px;
}
.operation-log-hide-jobs {
align-items: center;
min-height: 31px;
}
.operation-log-settings-actions {
display: flex;
flex-wrap: wrap;

File diff suppressed because one or more lines are too long