2025-11-24 14:05:30 +08:00

209 lines
7.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import time
import threading
from flask import Flask, render_template, send_from_directory, request, jsonify
from flask_cors import CORS
import chat_splitter
import analyzer
# --- 1. 配置区域 ---
# 定义项目根目录 (F:\工作\项目\G36\官网)
ROOT_PROJECT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
app = Flask(__name__, static_folder=ROOT_PROJECT_DIR, static_url_path='/')
CORS(app) # 启用CORS允许来自所有源的请求
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
UPLOADS_FOLDER = os.path.join(BASE_DIR, 'uploads')
SPLIT_LOGS_FOLDER = os.path.join(BASE_DIR, '聊天记录_按天拆分')
REPORTS_FOLDER = os.path.join(BASE_DIR, '分析报告')
LOG_FILE = os.path.join(BASE_DIR, 'app_activity.log')
# -- 新增 --: 用于持久化存储上一次分析时间戳的状态文件
STATE_FILE = os.path.join(BASE_DIR, '.app_state')
CHECK_INTERVAL_SECONDS = 300
APP_CONFIG = {
"OPENROUTER_API_KEY": "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",
"YOUR_SITE_URL": "https://your-site.com",
"YOUR_SITE_NAME": "My Game Analysis",
"MODEL_NAME": "google/gemini-2.5-pro"
}
# 全局变量,将在启动时从状态文件加载
last_known_mtime = 0
# --- 2. 日志与状态管理 (核心修改区域) ---
def log_activity(message: str):
"""记录活动到日志文件并打印到控制台。"""
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
log_message = f"[{timestamp}] {message}"
print(log_message)
try:
with open(LOG_FILE, 'a', encoding='utf-8') as f:
f.write(log_message + '\\n')
except IOError as e:
print(f"[{timestamp}] CRITICAL: Unable to write to log file '{LOG_FILE}'. Error: {e}")
# -- 新增 --: 从状态文件加载上一次的修改时间
def load_last_mtime() -> float:
"""从.app_state文件加载上次记录的mtime如果文件不存在或无效则返回0。"""
try:
if os.path.exists(STATE_FILE):
with open(STATE_FILE, 'r') as f:
return float(f.read().strip())
except (IOError, ValueError) as e:
log_activity(f"Warning: Could not read state from '{STATE_FILE}', defaulting to 0. Error: {e}")
return 0.0
# -- 新增 --: 将最新的修改时间保存到状态文件
def save_last_mtime(mtime: float):
"""将新的mtime保存到.app_state文件。"""
try:
with open(STATE_FILE, 'w') as f:
f.write(str(mtime))
except IOError as e:
log_activity(f"Warning: Could not save state to '{STATE_FILE}'. Error: {e}")
# --- 3. 后台自动分析任务 ---
def get_latest_mtime_in_folder(folder_path: str) -> float:
"""获取指定文件夹内所有.txt文件的最新修改时间。"""
latest_mtime = 0
try:
txt_files = [f for f in os.listdir(folder_path) if f.endswith('.txt')]
if not txt_files: return 0
for filename in txt_files:
file_path = os.path.join(folder_path, filename)
try:
mtime = os.path.getmtime(file_path)
if mtime > latest_mtime:
latest_mtime = mtime
except FileNotFoundError:
continue
except FileNotFoundError:
return 0
return latest_mtime
def run_the_analysis():
"""封装核心分析逻辑,以便可以被手动调用。"""
global last_known_mtime
log_activity("--- Triggering analysis manually. ---")
current_latest_mtime = get_latest_mtime_in_folder(UPLOADS_FOLDER)
log_activity("Step 1/2: Merging and splitting all chat logs by day...")
chat_splitter.split_log_file(UPLOADS_FOLDER, SPLIT_LOGS_FOLDER)
log_activity("Step 2/2: Analyzing new daily log files...")
new_reports_count = analyzer.run_analysis_on_new_files(
input_folder=SPLIT_LOGS_FOLDER,
output_folder=REPORTS_FOLDER,
config=APP_CONFIG
)
if new_reports_count > 0:
log_activity(f"--- SUCCESS: Analysis complete. Generated/Updated {new_reports_count} report(s). ---")
else:
log_activity("--- Analysis complete. No new reports were generated. ---")
last_known_mtime = current_latest_mtime
save_last_mtime(last_known_mtime)
log_activity(f"State updated. New last_known_mtime is {last_known_mtime}.")
return new_reports_count
def background_analysis_task():
"""在后台运行的守护线程定时检查uploads文件夹中是否有文件更新并触发分析。"""
global last_known_mtime
log_activity(f"Background monitoring started. Checking '{UPLOADS_FOLDER}' for updates...")
while True:
try:
current_latest_mtime = get_latest_mtime_in_folder(UPLOADS_FOLDER)
if current_latest_mtime > last_known_mtime:
run_the_analysis()
except Exception as e:
log_activity(f"CRITICAL ERROR in background task: {e}")
time.sleep(CHECK_INTERVAL_SECONDS)
# --- 4. API 路由 ---
@app.route('/')
def index():
return app.send_static_file('index.html')
# 为报告文件夹提供一个专门的路由,以便前端可以访问
@app.route('/网页服务/分析报告/<path:filename>')
def serve_report_file(filename):
return send_from_directory(REPORTS_FOLDER, filename)
@app.route('/reports', methods=['GET'])
def list_reports():
"""提供一个API端点用于列出所有报告包含修改时间"""
reports = []
if not os.path.exists(REPORTS_FOLDER):
return jsonify([])
for filename in sorted(os.listdir(REPORTS_FOLDER), reverse=True):
if filename.endswith(".html"):
filepath = os.path.join(REPORTS_FOLDER, filename)
reports.append({
"name": filename,
"mtime": os.path.getmtime(filepath)
})
return jsonify(reports)
@app.route('/analyze', methods=['POST'])
def handle_analysis_request():
"""处理来自前端的文件上传和分析请求。"""
if 'file' not in request.files:
return jsonify({"error": "No file part"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "No selected file"}), 400
if file and file.filename.endswith('.txt'):
try:
filepath = os.path.join(UPLOADS_FOLDER, file.filename)
file.save(filepath)
log_activity(f"Received new file from web: '{file.filename}'. Saved to uploads folder.")
# 在一个新线程中运行分析,避免阻塞网页请求
analysis_thread = threading.Thread(target=run_the_analysis)
analysis_thread.start()
return jsonify({"message": f"File '{file.filename}' uploaded successfully. Analysis started in background."}), 202
except Exception as e:
log_activity(f"Error processing uploaded file: {e}")
return jsonify({"error": "Server error during file processing"}), 500
return jsonify({"error": "Invalid file type, only .txt is accepted"}), 400
# --- 5. 启动应用 (核心修改区域) ---
if __name__ == '__main__':
for folder in [UPLOADS_FOLDER, SPLIT_LOGS_FOLDER, REPORTS_FOLDER]:
if not os.path.exists(folder):
os.makedirs(folder)
# -- 修改 --: 在启动时从文件加载“记忆”,而不是实时获取
last_known_mtime = load_last_mtime()
log_activity(f"Application starting... Loaded last known mtime from state file: {last_known_mtime}.")
daemon = threading.Thread(target=background_analysis_task, daemon=True, name='Monitor')
daemon.start()
log_activity("Web service started. Background task is now monitoring for file changes.")
log_activity(f"To begin, please place your chat log files (.txt) into the '{UPLOADS_FOLDER}' folder.")
app.run(host='0.0.0.0', port=5000, use_reloader=False)