feat: add mastery feature to recitation wheel
This commit is contained in:
57
web_app.py
57
web_app.py
@@ -18,14 +18,25 @@ from modules.ai_analysis import AIAnalysisModule
|
||||
from modules.recommendation_engine import RecommendationEngine
|
||||
from modules.ocr_calorie_recognition import OCRCalorieRecognitionModule
|
||||
|
||||
# 配置日志
|
||||
# 配置日志 - 确保UTF-8编码
|
||||
import sys
|
||||
# 设置标准输出编码为UTF-8
|
||||
if sys.stdout.encoding != 'utf-8':
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
if sys.stderr.encoding != 'utf-8':
|
||||
sys.stderr.reconfigure(encoding='utf-8')
|
||||
|
||||
# 创建logs目录
|
||||
Path('logs').mkdir(exist_ok=True)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('logs/web_app.log', encoding='utf-8'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
logging.StreamHandler(sys.stdout)
|
||||
],
|
||||
force=True # 强制重新配置
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -35,6 +46,9 @@ app.config['SECRET_KEY'] = 'your-secret-key-here'
|
||||
# 确保模板文件使用UTF-8编码读取
|
||||
app.jinja_env.auto_reload = True
|
||||
app.config['TEMPLATES_AUTO_RELOAD'] = True
|
||||
# 设置Jinja2模板加载器使用UTF-8编码
|
||||
from jinja2 import FileSystemLoader
|
||||
app.jinja_loader = FileSystemLoader('templates', encoding='utf-8')
|
||||
|
||||
# 确保所有响应使用UTF-8编码
|
||||
@app.after_request
|
||||
@@ -156,31 +170,31 @@ sorter = RecitationSorter()
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""首页"""
|
||||
return render_template('index.html', encoding='utf-8')
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@app.route('/recitation')
|
||||
def recitation():
|
||||
"""背诵排序页面"""
|
||||
return render_template('recitation.html', encoding='utf-8')
|
||||
return render_template('recitation.html')
|
||||
|
||||
|
||||
@app.route('/data-collection')
|
||||
def data_collection():
|
||||
"""数据采集页面"""
|
||||
return render_template('data_collection.html', encoding='utf-8')
|
||||
return render_template('data_collection.html')
|
||||
|
||||
|
||||
@app.route('/recommendation')
|
||||
def recommendation():
|
||||
"""推荐页面"""
|
||||
return render_template('recommendation.html', encoding='utf-8')
|
||||
return render_template('recommendation.html')
|
||||
|
||||
|
||||
@app.route('/analysis')
|
||||
def analysis():
|
||||
"""分析页面"""
|
||||
return render_template('analysis.html', encoding='utf-8')
|
||||
return render_template('analysis.html')
|
||||
|
||||
|
||||
@app.route('/api/extract', methods=['POST'])
|
||||
@@ -295,11 +309,36 @@ def export_sorted():
|
||||
filename = f'背诵排序结果_{datetime.now().strftime("%Y%m%d_%H%M%S")}.txt'
|
||||
mimetype = 'text/plain; charset=utf-8'
|
||||
|
||||
# 修复文件名编码问题:HTTP头必须使用latin-1编码
|
||||
# 使用RFC 5987标准编码中文文件名
|
||||
from urllib.parse import quote
|
||||
|
||||
# ASCII fallback文件名(确保兼容性)
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
fallback_filename = f'recitation_sorted_{timestamp}.{export_format}'
|
||||
|
||||
# RFC 5987编码:对UTF-8字节序列进行百分号编码
|
||||
# 格式: filename="fallback"; filename*=UTF-8''encoded
|
||||
# 将所有字节都进行百分号编码,确保HTTP头的latin-1兼容性
|
||||
try:
|
||||
utf8_bytes = filename.encode('utf-8')
|
||||
# 对所有字节进行百分号编码(大写十六进制)
|
||||
encoded_filename = ''.join([f'%{b:02X}' for b in utf8_bytes])
|
||||
except Exception as e:
|
||||
logger.warning(f"文件名编码失败,使用fallback: {e}")
|
||||
encoded_filename = fallback_filename
|
||||
|
||||
# 构建Content-Disposition头:同时提供fallback和UTF-8编码版本
|
||||
content_disposition = (
|
||||
f'attachment; filename="{fallback_filename}"; '
|
||||
f"filename*=UTF-8''{encoded_filename}"
|
||||
)
|
||||
|
||||
response = Response(
|
||||
content.encode('utf-8'),
|
||||
mimetype=mimetype,
|
||||
headers={
|
||||
'Content-Disposition': f'attachment; filename="{filename}"'
|
||||
'Content-Disposition': content_disposition
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user