223 lines
8.1 KiB
Python

"""Logger view."""
import csv
import io
import datetime
from flask import Blueprint, render_template, request, send_file, jsonify
from ..db import db
from ..models import ActivityLog, ActivityCategory
bp = Blueprint("logger", __name__, url_prefix="/logs")
@bp.route("/")
def list_logs():
# For the new modern view, we only need to provide initial filter values and categories
# The actual data loading will be handled by JavaScript via the API endpoint
# Get filter parameters for initial state
categories_param = request.args.getlist("category") # Get multiple categories
start_date = request.args.get("start_date")
end_date = request.args.get("end_date")
search_term = request.args.get("search_term")
if search_term == "None":
search_term = None
categories = [e.value for e in ActivityCategory]
return render_template(
"logs.html.jinja",
categories=categories,
selected_categories=categories_param, # Pass selected categories
start_date=start_date,
end_date=end_date,
search_term=search_term,
app_title="PaperScraper",
)
@bp.route("/download")
def download_logs():
# Filters - reuse logic from list_logs
categories = request.args.getlist("category") # Get multiple categories
start_date = request.args.get("start_date")
end_date = request.args.get("end_date")
search_term = request.args.get("search_term")
query = ActivityLog.query
if categories:
query = query.filter(ActivityLog.category.in_(categories))
if start_date:
start_date_dt = datetime.datetime.strptime(start_date, "%Y-%m-%d")
query = query.filter(ActivityLog.timestamp >= start_date_dt)
if end_date:
end_date_dt = datetime.datetime.strptime(end_date, "%Y-%m-%d") + datetime.timedelta(days=1)
query = query.filter(ActivityLog.timestamp <= end_date_dt)
if search_term:
query = query.filter(db.or_(
ActivityLog.action.contains(search_term),
ActivityLog.description.contains(search_term)
))
logs = query.order_by(ActivityLog.timestamp.desc()).all()
# Prepare CSV data
csv_data = io.StringIO()
csv_writer = csv.writer(csv_data)
csv_writer.writerow(["Timestamp", "Category", "Action", "Description", "Extra Data"]) # Header
for log in logs:
csv_writer.writerow([
log.timestamp,
log.category,
log.action,
log.description,
log.extra_data # Consider formatting this better
])
# Create response
filename = f"logs_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
csv_data.seek(0)
output = io.BytesIO(csv_data.getvalue().encode('utf-8'))
output.seek(0)
return send_file(
output,
mimetype="text/csv",
as_attachment=True,
download_name=filename
)
@bp.route("/<int:log_id>/detail")
def log_detail(log_id):
log = ActivityLog.query.get_or_404(log_id)
return render_template("partials/log_detail_modal.html.jinja", log=log)
@bp.route("/api")
def get_logs_api():
"""Unified API endpoint for getting activity logs with filtering and pagination support."""
try:
# Pagination parameters
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 50, type=int)
# Legacy limit parameter for backward compatibility
limit = request.args.get('limit', type=int)
if limit and not request.args.get('page'):
# Legacy mode: use limit without pagination
query = ActivityLog.query
# Apply filters
categories = request.args.getlist('category')
if categories:
query = query.filter(ActivityLog.category.in_(categories))
status = request.args.get('status')
if status:
query = query.filter(ActivityLog.status == status)
start_date = request.args.get('start_date')
if start_date:
start_date_dt = datetime.datetime.strptime(start_date, "%Y-%m-%d")
query = query.filter(ActivityLog.timestamp >= start_date_dt)
end_date = request.args.get('end_date')
if end_date:
end_date_dt = datetime.datetime.strptime(end_date, "%Y-%m-%d") + datetime.timedelta(days=1)
query = query.filter(ActivityLog.timestamp <= end_date_dt)
search_term = request.args.get('search_term')
if search_term and search_term != "None":
query = query.filter(db.or_(
ActivityLog.action.contains(search_term),
ActivityLog.description.contains(search_term)
))
logs = query.order_by(ActivityLog.timestamp.desc()).limit(limit).all()
return jsonify({
"success": True,
"logs": [{
"id": log.id,
"timestamp": log.timestamp.isoformat(),
"action": log.action,
"status": log.status,
"description": log.description,
"category": log.category,
"paper_id": log.paper_id,
"extra_data": log.extra_data
} for log in logs]
})
# Ensure reasonable per_page limits
per_page = min(per_page, 100) # Cap at 100 items per page
# Build query with filtering
query = ActivityLog.query
# Filter by categories if specified
categories = request.args.getlist('category')
if categories:
query = query.filter(ActivityLog.category.in_(categories))
# Filter by status if specified
status = request.args.get('status')
if status:
query = query.filter(ActivityLog.status == status)
# Date filters
start_date = request.args.get('start_date')
if start_date:
start_date_dt = datetime.datetime.strptime(start_date, "%Y-%m-%d")
query = query.filter(ActivityLog.timestamp >= start_date_dt)
end_date = request.args.get('end_date')
if end_date:
end_date_dt = datetime.datetime.strptime(end_date, "%Y-%m-%d") + datetime.timedelta(days=1)
query = query.filter(ActivityLog.timestamp <= end_date_dt)
# Search term filter
search_term = request.args.get('search_term')
if search_term and search_term != "None":
query = query.filter(db.or_(
ActivityLog.action.contains(search_term),
ActivityLog.description.contains(search_term)
))
# Order by most recent first and paginate
pagination = query.order_by(ActivityLog.timestamp.desc()).paginate(
page=page,
per_page=per_page,
error_out=False
)
return jsonify({
"success": True,
"logs": [{
"id": log.id,
"timestamp": log.timestamp.isoformat(),
"action": log.action,
"status": log.status,
"description": log.description,
"category": log.category,
"paper_id": log.paper_id,
"extra_data": log.extra_data
} for log in pagination.items],
"pagination": {
"page": pagination.page,
"pages": pagination.pages,
"per_page": pagination.per_page,
"total": pagination.total,
"has_next": pagination.has_next,
"has_prev": pagination.has_prev,
"next_num": pagination.next_num if pagination.has_next else None,
"prev_num": pagination.prev_num if pagination.has_prev else None
}
})
except Exception as e:
return jsonify({
"success": False,
"message": f"Error getting logs: {str(e)}"
}), 500