Spaces:
Sleeping
Sleeping
| from pathlib import Path | |
| from flask import Flask, jsonify, render_template, request, send_file | |
| from werkzeug.exceptions import RequestEntityTooLarge | |
| from aging_tool.service import ( | |
| AgingSummaryError, | |
| AnalysisTaskNotFound as AgingTaskNotFound, | |
| get_task_status as get_aging_task_status, | |
| start_analysis_task as start_aging_analysis_task, | |
| ) | |
| from keyword_tool.service import ( | |
| AnalysisTaskNotFound as KeywordTaskNotFound, | |
| KeywordSummaryError, | |
| get_task_status as get_keyword_task_status, | |
| start_analysis_task as start_keyword_analysis_task, | |
| ) | |
| from sguid_tool.processor import ProcessingError | |
| from sguid_tool.service import ( | |
| AnalysisTaskNotFound as SguidTaskNotFound, | |
| get_task_status as get_sguid_task_status, | |
| start_analysis_task as start_sguid_analysis_task, | |
| ) | |
| from sguid_tool.stats import get_usage_stats, record_usage | |
| BASE_DIR = Path(__file__).resolve().parent | |
| def create_app() -> Flask: | |
| app = Flask(__name__) | |
| app.config["MAX_CONTENT_LENGTH"] = 100 * 1024 * 1024 | |
| app.config["UPLOAD_FOLDER"] = BASE_DIR / "uploads" | |
| app.config["OUTPUT_FOLDER"] = BASE_DIR / "saved_outputs" | |
| app.config["DATA_FOLDER"] = BASE_DIR / "data" | |
| app.config["KEYWORD_UPLOAD_FOLDER"] = BASE_DIR / "uploads" / "keyword_summary" | |
| app.config["KEYWORD_OUTPUT_FOLDER"] = BASE_DIR / "saved_outputs" / "keyword_summary" | |
| app.config["AGING_UPLOAD_FOLDER"] = BASE_DIR / "uploads" / "aging_stock_summary" | |
| app.config["AGING_OUTPUT_FOLDER"] = BASE_DIR / "saved_outputs" / "aging_stock_summary" | |
| for key in [ | |
| "UPLOAD_FOLDER", | |
| "OUTPUT_FOLDER", | |
| "DATA_FOLDER", | |
| "KEYWORD_UPLOAD_FOLDER", | |
| "KEYWORD_OUTPUT_FOLDER", | |
| "AGING_UPLOAD_FOLDER", | |
| "AGING_OUTPUT_FOLDER", | |
| ]: | |
| app.config[key].mkdir(parents=True, exist_ok=True) | |
| def index(): | |
| return render_template("index.html", active_nav="sgu") | |
| def keyword_summary(): | |
| return render_template("keyword_summary.html", active_nav="keyword") | |
| def placement_summary(): | |
| return render_template("keyword_summary.html", active_nav="placement") | |
| def aging_stock(): | |
| return render_template("aging_stock.html", active_nav="aging") | |
| def stats(): | |
| return jsonify(get_usage_stats(app.config["DATA_FOLDER"])) | |
| def process_file(): | |
| uploaded_file = request.files.get("file") | |
| if not uploaded_file or not uploaded_file.filename: | |
| return jsonify({"success": False, "error": "请选择一个 .xlsx 或 .csv 文件。"}), 400 | |
| record_usage(app.config["DATA_FOLDER"]) | |
| try: | |
| task_id = start_sguid_analysis_task( | |
| uploaded_file=uploaded_file, | |
| upload_dir=app.config["UPLOAD_FOLDER"], | |
| output_dir=app.config["OUTPUT_FOLDER"], | |
| ) | |
| except ProcessingError as exc: | |
| return jsonify({"success": False, "error": str(exc)}), 400 | |
| return jsonify({"success": True, "task_id": task_id, "message": "文件上传成功,正在生成 SGU 汇总结果..."}) | |
| def process_file_status(task_id: str): | |
| try: | |
| return jsonify(get_sguid_task_status(task_id)) | |
| except SguidTaskNotFound as exc: | |
| return jsonify({"success": False, "error": str(exc)}), 404 | |
| def process_file_download(filename: str): | |
| file_path = app.config["OUTPUT_FOLDER"] / filename | |
| if not file_path.exists(): | |
| return jsonify({"success": False, "error": "结果文件不存在。"}), 404 | |
| return send_file( | |
| file_path, | |
| as_attachment=True, | |
| download_name=filename, | |
| mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| ) | |
| def keyword_summary_upload(): | |
| uploaded_file = request.files.get("file") | |
| if not uploaded_file or not uploaded_file.filename: | |
| return jsonify({"success": False, "error": "请选择一个 Excel 或 CSV 文件。"}), 400 | |
| record_usage(app.config["DATA_FOLDER"]) | |
| try: | |
| task_id = start_keyword_analysis_task( | |
| uploaded_file=uploaded_file, | |
| upload_dir=app.config["KEYWORD_UPLOAD_FOLDER"], | |
| output_dir=app.config["KEYWORD_OUTPUT_FOLDER"], | |
| ) | |
| except KeywordSummaryError as exc: | |
| return jsonify({"success": False, "error": str(exc)}), 400 | |
| return jsonify( | |
| { | |
| "success": True, | |
| "task_id": task_id, | |
| "message": "文件上传成功,正在生成搜索词与投放词汇总...", | |
| } | |
| ) | |
| def keyword_summary_status(task_id: str): | |
| try: | |
| return jsonify(get_keyword_task_status(task_id)) | |
| except KeywordTaskNotFound as exc: | |
| return jsonify({"success": False, "error": str(exc)}), 404 | |
| def keyword_summary_download(filename: str): | |
| file_path = app.config["KEYWORD_OUTPUT_FOLDER"] / filename | |
| if not file_path.exists(): | |
| return jsonify({"success": False, "error": "结果文件不存在。"}), 404 | |
| return send_file( | |
| file_path, | |
| as_attachment=True, | |
| download_name=filename, | |
| mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| ) | |
| def aging_stock_upload(): | |
| uploaded_file = request.files.get("file") | |
| if not uploaded_file or not uploaded_file.filename: | |
| return jsonify({"success": False, "error": "请选择库龄 Excel 或 CSV 文件。"}), 400 | |
| record_usage(app.config["DATA_FOLDER"]) | |
| try: | |
| task_id = start_aging_analysis_task( | |
| uploaded_file=uploaded_file, | |
| upload_dir=app.config["AGING_UPLOAD_FOLDER"], | |
| output_dir=app.config["AGING_OUTPUT_FOLDER"], | |
| ) | |
| except AgingSummaryError as exc: | |
| return jsonify({"success": False, "error": str(exc)}), 400 | |
| return jsonify({"success": True, "task_id": task_id, "message": "文件上传成功,正在汇总超龄库存数据..."}) | |
| def aging_stock_status(task_id: str): | |
| try: | |
| return jsonify(get_aging_task_status(task_id)) | |
| except AgingTaskNotFound as exc: | |
| return jsonify({"success": False, "error": str(exc)}), 404 | |
| def aging_stock_download(filename: str): | |
| file_path = app.config["AGING_OUTPUT_FOLDER"] / filename | |
| if not file_path.exists(): | |
| return jsonify({"success": False, "error": "结果文件不存在。"}), 404 | |
| return send_file( | |
| file_path, | |
| as_attachment=True, | |
| download_name=filename, | |
| mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| ) | |
| def handle_large_file(_error): | |
| return jsonify({"message": "上传文件过大,当前限制为 100MB,请压缩源文件后重试。"}), 413 | |
| return app | |
| app = create_app() | |
| if __name__ == "__main__": | |
| app.run(host="0.0.0.0", port=5001, debug=True) | |