diff --git a/edc-web/app/models.py b/edc-web/app/models.py index 511ebca..bb586bf 100644 --- a/edc-web/app/models.py +++ b/edc-web/app/models.py @@ -832,7 +832,8 @@ def delete_test_data(serial: str = "", date_from: str = "", # ─── tb_device_log ───────────────────────────────────────────────── def get_device_logs(page: int = 1, per_page: int = 30, - serial: str = "", event_type: str = "") -> tuple[list[dict], int]: + serial: str = "", event_type: str = "", + date_from: str = "", date_to: str = "") -> tuple[list[dict], int]: """分页查询设备事件日志,返回 (records, total)""" conn = get_conn() try: @@ -845,6 +846,12 @@ def get_device_logs(page: int = 1, per_page: int = 30, if event_type: where.append("event_type = %s") params.append(event_type) + if date_from: + where.append("create_time >= %s") + params.append(date_from if len(date_from) > 10 else date_from) + if date_to: + where.append("create_time <= %s") + params.append(date_to if len(date_to) > 10 else date_to + " 23:59:59") where_clause = " AND ".join(where) if where else "1=1" @@ -865,6 +872,38 @@ def get_device_logs(page: int = 1, per_page: int = 30, conn.close() +def export_device_logs(serial: str = "", event_type: str = "", + date_from: str = "", date_to: str = "") -> list[dict]: + """导出全部设备事件日志(不分页)""" + conn = get_conn() + try: + with conn.cursor() as cur: + where = [] + params = [] + if serial: + where.append("device_serial LIKE %s") + params.append(f"%{serial}%") + if event_type: + where.append("event_type = %s") + params.append(event_type) + if date_from: + where.append("create_time >= %s") + params.append(date_from if len(date_from) > 10 else date_from) + if date_to: + where.append("create_time <= %s") + params.append(date_to if len(date_to) > 10 else date_to + " 23:59:59") + + where_clause = " AND ".join(where) if where else "1=1" + cur.execute( + f"SELECT * FROM tb_device_log WHERE {where_clause} " + f"ORDER BY id DESC", + params, + ) + return cur.fetchall() + finally: + conn.close() + + def delete_device_logs(serial: str = "", event_type: str = "", date_from: str = "", date_to: str = "") -> int: """删除符合条件的设备日志,返回删除行数。至少需要一个条件。""" diff --git a/edc-web/app/routes/device_logs.py b/edc-web/app/routes/device_logs.py index 20b1f75..71a9c01 100644 --- a/edc-web/app/routes/device_logs.py +++ b/edc-web/app/routes/device_logs.py @@ -1,8 +1,11 @@ """设备事件日志 API""" -from flask import Blueprint, jsonify, render_template, request +import csv +import io + +from flask import Blueprint, jsonify, render_template, request, Response from flask_login import login_required, current_user -from app.models import get_device_logs, delete_device_logs, insert_log +from app.models import get_device_logs, export_device_logs, delete_device_logs, insert_log bp = Blueprint("device_logs", __name__) @@ -22,15 +25,49 @@ def api_device_logs(): per_page = request.args.get("per_page", 30, type=int) serial = request.args.get("serial", "", type=str) event_type = request.args.get("event_type", "", type=str) + date_from = request.args.get("date_from", "", type=str) + date_to = request.args.get("date_to", "", type=str) records, total = get_device_logs( page=page, per_page=per_page, serial=serial, event_type=event_type, + date_from=date_from, date_to=date_to, ) pages = max(1, (total + per_page - 1) // per_page) return jsonify({"records": records, "total": total, "pages": pages}) +@bp.route("/api/device-logs/export") +@login_required +def api_export(): + """导出设备事件日志为 CSV""" + serial = request.args.get("serial", "", type=str) + event_type = request.args.get("event_type", "", type=str) + date_from = request.args.get("date_from", "", type=str) + date_to = request.args.get("date_to", "", type=str) + + records = export_device_logs( + serial=serial, event_type=event_type, + date_from=date_from, date_to=date_to, + ) + + output = io.StringIO() + writer = csv.writer(output) + + if records: + headers = list(records[0].keys()) + writer.writerow(headers) + for r in records: + writer.writerow(r.values()) + + output.seek(0) + return Response( + output.getvalue(), + mimetype="text/csv", + headers={"Content-Disposition": "attachment; filename=device_logs.csv"}, + ) + + @bp.route("/api/device-logs/delete", methods=["POST"]) @login_required def api_device_logs_delete(): diff --git a/edc-web/app/templates/device_logs.html b/edc-web/app/templates/device_logs.html index c651cf2..4686df3 100644 --- a/edc-web/app/templates/device_logs.html +++ b/edc-web/app/templates/device_logs.html @@ -17,7 +17,16 @@ + + {% if current_user.role == 'admin' %} {% endif %} @@ -44,13 +53,25 @@