""" DIT 分析 API 路由 """ import os import json from flask import Blueprint, request, jsonify, current_app from werkzeug.utils import secure_filename from services.dit_analyzer import DITAnalyzer, DITAnalyzerError api_bp = Blueprint('api', __name__, url_prefix='/api') ALLOWED_EXTENSIONS = {'csv'} UPLOAD_FOLDER = 'uploads' def allowed_file(filename: str) -> bool: """檢查檔案副檔名""" return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @api_bp.route('/analyze', methods=['POST']) def analyze_dit(): """ 分析 DIT CSV 檔案 接受 multipart/form-data 上傳 CSV 回傳 JSON 格式分析結果 """ # 檢查檔案 if 'file' not in request.files: return jsonify({"error": "未上傳檔案", "code": "NO_FILE"}), 400 file = request.files['file'] if file.filename == '': return jsonify({"error": "未選擇檔案", "code": "NO_FILENAME"}), 400 if not allowed_file(file.filename): return jsonify({"error": "僅支援 CSV 檔案", "code": "INVALID_TYPE"}), 400 # 取得參數 top_percent = float(request.form.get('top_percent', 0.2)) low_win_rate = float(request.form.get('low_win_rate', 0.1)) threshold_days = int(request.form.get('threshold_days', 60)) try: # 確保上傳目錄存在 os.makedirs(UPLOAD_FOLDER, exist_ok=True) # 儲存檔案 filename = secure_filename(file.filename) filepath = os.path.join(UPLOAD_FOLDER, filename) file.save(filepath) # 執行分析 analyzer = DITAnalyzer(filepath) report = analyzer.generate_report( top_percent=top_percent, low_win_rate=low_win_rate, threshold_days=threshold_days ) # 清理暫存檔 os.remove(filepath) return jsonify({ "status": "success", "data": report }) except DITAnalyzerError as e: return jsonify({"error": str(e), "code": "ANALYZER_ERROR"}), 400 except Exception as e: return jsonify({"error": f"分析失敗: {str(e)}", "code": "INTERNAL_ERROR"}), 500 @api_bp.route('/analyze/resource-allocation', methods=['POST']) def analyze_resource_allocation(): """ 僅執行 Feature 6.2: 高價值資源分配分析 """ if 'file' not in request.files: return jsonify({"error": "未上傳檔案", "code": "NO_FILE"}), 400 file = request.files['file'] if not allowed_file(file.filename): return jsonify({"error": "僅支援 CSV 檔案", "code": "INVALID_TYPE"}), 400 top_percent = float(request.form.get('top_percent', 0.2)) low_win_rate = float(request.form.get('low_win_rate', 0.1)) try: os.makedirs(UPLOAD_FOLDER, exist_ok=True) filename = secure_filename(file.filename) filepath = os.path.join(UPLOAD_FOLDER, filename) file.save(filepath) analyzer = DITAnalyzer(filepath) results = analyzer.analyze_resource_allocation(top_percent, low_win_rate) os.remove(filepath) return jsonify({ "status": "success", "data": { "type": "resource_allocation", "count": len(results), "action_cards": results } }) except DITAnalyzerError as e: return jsonify({"error": str(e), "code": "ANALYZER_ERROR"}), 400 except Exception as e: return jsonify({"error": f"分析失敗: {str(e)}", "code": "INTERNAL_ERROR"}), 500 @api_bp.route('/analyze/stagnant-deals', methods=['POST']) def analyze_stagnant_deals(): """ 僅執行 Feature 6.3: 呆滯案件警示 """ if 'file' not in request.files: return jsonify({"error": "未上傳檔案", "code": "NO_FILE"}), 400 file = request.files['file'] if not allowed_file(file.filename): return jsonify({"error": "僅支援 CSV 檔案", "code": "INVALID_TYPE"}), 400 threshold_days = int(request.form.get('threshold_days', 60)) try: os.makedirs(UPLOAD_FOLDER, exist_ok=True) filename = secure_filename(file.filename) filepath = os.path.join(UPLOAD_FOLDER, filename) file.save(filepath) analyzer = DITAnalyzer(filepath) results = analyzer.analyze_stagnant_deals(threshold_days) os.remove(filepath) return jsonify({ "status": "success", "data": { "type": "stagnant_deals", "count": len(results), "action_cards": results } }) except DITAnalyzerError as e: return jsonify({"error": str(e), "code": "ANALYZER_ERROR"}), 400 except Exception as e: return jsonify({"error": f"分析失敗: {str(e)}", "code": "INTERNAL_ERROR"}), 500