From bfc640118ec233d2216f0ca4c2d8d207d3c8cb85 Mon Sep 17 00:00:00 2001 From: pagfcvmb9 <1325529045@qq.com> Date: Fri, 21 Nov 2025 22:26:23 +0800 Subject: [PATCH] Update app.py --- app.py | 268 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 174 insertions(+), 94 deletions(-) diff --git a/app.py b/app.py index 3796993..56c7684 100644 --- a/app.py +++ b/app.py @@ -1,95 +1,175 @@ -from flask import Flask, render_template, request, jsonify, send_file -from services import RollCallService, ScoreService -import io - -app = Flask(__name__) -roll_call_service = RollCallService() -score_service = ScoreService() - -@app.route('/') -def index(): - """主页面""" - return render_template('index.html') - -@app.route('/roll_call') -def roll_call_page(): - """点名页面""" - return render_template('roll_call.html') - -@app.route('/scores') -def scores_page(): - """积分页面""" - return render_template('scores.html') - -# API接口 -@app.route('/api/roll_call/random', methods=['POST']) -def random_roll_call(): - """随机点名API""" - class_name = request.json.get('class_name', '软工K班') - student = roll_call_service.random_roll_call(class_name) - - if student: - return jsonify({ - 'success': True, - 'data': student.to_dict() - }) - else: - return jsonify({ - 'success': False, - 'message': '没有找到学生' - }) - -@app.route('/api/scores/update', methods=['POST']) -def update_score(): - """更新积分API""" - data = request.json - student_id = data.get('student_id') - answer_type = data.get('answer_type') - performance = data.get('performance') - - success, score_delta = roll_call_service.update_student_score( - student_id, answer_type, performance - ) - - return jsonify({ - 'success': success, - 'score_delta': score_delta - }) - -@app.route('/api/scores/ranking') -def get_ranking(): - """获取积分排名API""" - class_name = request.args.get('class_name', '软工K班') - ranking = score_service.get_class_ranking(class_name) - - return jsonify({ - 'success': True, - 'data': [student.to_dict() for student in ranking] - }) - -@app.route('/api/scores/export') -def export_scores(): - """导出积分Excel API""" - class_name = request.args.get('class_name', '软工K班') - excel_data = score_service.export_scores_excel(class_name) - - return send_file( - excel_data, - mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - as_attachment=True, - download_name=f'{class_name}_积分统计.xlsx' - ) - -@app.route('/api/roll_call/history') -def get_roll_call_history(): - """获取点名历史API""" - db = roll_call_service.db - recent_records = db.get_recent_records(10) - - return jsonify({ - 'success': True, - 'data': recent_records - }) - -if __name__ == '__main__': +from flask import Flask, render_template, request, jsonify, send_file, redirect, url_for, flash +from services import RollCallService, ScoreService +import io +from models import init_db, get_db, Student # 新增导入 +import pandas as pd # 新增导入 + +# 初始化数据库(首次运行创建表) +init_db() + +app = Flask(__name__) +app.secret_key = 'your_secret_key' # 用于flash提示 +roll_call_service = RollCallService() +score_service = ScoreService() + + +# 首页 +@app.route('/') +def index(): + return render_template('index.html') + + +# 点名页面(支持选择模式) +@app.route('/roll_call') +def roll_call_page(): + return render_template('roll_call.html') + + +# 积分页面(含可视化) +@app.route('/scores') +def scores_page(): + class_name = request.args.get('class_name', '软工K班') + chart_base64 = score_service.generate_score_chart(class_name) + return render_template('scores.html', chart_base64=chart_base64, class_name=class_name) + + +# 新增:Excel导入页面 +@app.route('/import') +def import_page(): + return render_template('import.html') + + +# API:Excel导入学生 - 修复版本 +@app.route('/api/students/import', methods=['POST']) +def import_students(): # 移除嵌套的函数定义 + try: + # 检查文件是否存在 + if 'excel_file' not in request.files: + return jsonify({'success': False, 'message': '未选择文件'}) + + file = request.files['excel_file'] + + # 检查文件名 + if file.filename == '': + return jsonify({'success': False, 'message': '未选择文件'}) + + # 检查文件格式 + if not (file.filename.endswith('.xlsx') or file.filename.endswith('.xls')): + return jsonify({'success': False, 'message': '只支持 .xlsx 或 .xls 格式'}) + + # 读取Excel文件 + df = pd.read_excel(file) + + required_columns = ['学号', '姓名'] + missing_columns = [col for col in required_columns if col not in df.columns] + + if missing_columns: + return jsonify({ + 'success': False, + 'message': f'缺少必要列: {", ".join(missing_columns)}' + }) + + # 检查数据是否为空 + if df.empty: + return jsonify({'success': False, 'message': 'Excel文件为空'}) + + # 数据验证 + for index, row in df.iterrows(): + if pd.isna(row['学号']) or pd.isna(row['姓名']): + return jsonify({ + 'success': False, + 'message': f'第{index + 2}行数据不完整' + }) + + # 获取数据库会话 + db = next(get_db()) + + # 保存到数据库 + students_imported = 0 + for index, row in df.iterrows(): + # 检查学生是否已存在 + existing_student = db.query(Student).filter_by(student_id=str(row['学号'])).first() + + if not existing_student: + # 简化:只需要学号和姓名,其他字段使用默认值 + student = Student( + student_id=str(row['学号']), + name=row['姓名'], + major='软件工程', # 默认专业 + class_name='软工K班', # 默认班级 + total_score=0, + call_count=0 + ) + db.add(student) + students_imported += 1 + else: + # 如果学生已存在,更新姓名 + existing_student.name = row['姓名'] + + db.commit() + db.close() + + return jsonify({ + 'success': True, + 'message': f'成功导入 {students_imported} 名学生数据' + }) + + except Exception as e: + # 错误处理 + db.rollback() + db.close() + return jsonify({ + 'success': False, + 'message': f'导入失败: {str(e)}' + }) + + +# API:点名(支持random/order模式) +@app.route('/api/roll_call', methods=['POST']) +def roll_call(): + data = request.json + class_name = data.get('class_name', '软工K班') + call_mode = data.get('call_mode', 'random') # 新增模式参数 + student, msg = roll_call_service.roll_call(class_name, call_mode) + if student: + return jsonify({'success': True, 'data': student.to_dict()}) + return jsonify({'success': False, 'message': msg}) + + +# 其他原有API保持不变,仅更新积分更新接口 +@app.route('/api/scores/update', methods=['POST']) +def update_score(): + data = request.json + student_id = data.get('student_id') + answer_type = data.get('answer_type') + performance = data.get('performance') + status = data.get('status', 'present') # 新增到场状态 + success, score_delta = roll_call_service.update_student_score(student_id, answer_type, performance, status) + return jsonify({'success': success, 'score_delta': score_delta}) + + +# 新增:导出积分详单API(复用原有路由,内部逻辑已更新) +@app.route('/api/scores/export_detailed') +def export_detailed_scores(): + class_name = request.args.get('class_name', '软工K班') + excel_data = score_service.export_scores_excel(class_name) + return send_file( + excel_data, + mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + as_attachment=True, + download_name=f'{class_name}_积分详单.xlsx' + ) + + +@app.route('/api/scores/ranking') +def get_class_ranking(): + class_name = request.args.get('class_name', '软工K班') + students = score_service.get_class_ranking(class_name) + return jsonify({ + 'success': True, + 'data': [s.to_dict() for s in students] + }) + + +if __name__ == '__main__': app.run(debug=True, port=5000) \ No newline at end of file