diff --git a/app/routes/student.py b/app/routes/student.py index 7e45b04..597590a 100644 --- a/app/routes/student.py +++ b/app/routes/student.py @@ -6,6 +6,68 @@ from app.utils.database import safe_add_and_commit from datetime import datetime, timedelta from sqlalchemy import and_, or_, desc +# 这个文件包含需要在student.py中添加的缺勤次数计算函数 + +def calculate_absent_count_for_record(record): + """为单个考勤记录计算真实的缺勤次数""" + import json + from app.models import DailyAttendanceDetail + + # 获取该记录的每日明细 + daily_details = DailyAttendanceDetail.query.filter_by( + weekly_record_id=record.record_id + ).all() + + absent_count = 0 + + for detail in daily_details: + # 处理完全缺勤的情况 + if detail.status == '缺勤' and (not detail.remarks or not detail.remarks.startswith('{')): + # 完全缺勤的天数,早上+下午都缺勤 + absent_count += 2 # 早上缺勤1次 + 下午缺勤1次 + continue + + if detail.remarks and detail.remarks.startswith('{'): + try: + remarks_data = json.loads(detail.remarks) + details_info = remarks_data.get('details', {}) + + # 统计缺勤次数 + # 检查早上缺勤:morning_in AND morning_out 都missing + morning_data = details_info.get('morning', {}) + morning_in_time = morning_data.get('in') + morning_out_time = morning_data.get('out') + morning_in_status = morning_data.get('status', 'missing') + + if ((not morning_in_time or morning_in_status == 'missing') and + (not morning_out_time)): + absent_count += 1 + + # 检查下午缺勤:afternoon_in AND afternoon_out 都missing + afternoon_data = details_info.get('afternoon', {}) + afternoon_in_time = afternoon_data.get('in') + afternoon_out_time = afternoon_data.get('out') + afternoon_in_status = afternoon_data.get('status', 'missing') + + if ((not afternoon_in_time or afternoon_in_status == 'missing') and + (not afternoon_out_time)): + absent_count += 1 + + except (json.JSONDecodeError, KeyError, AttributeError): + continue + + return absent_count + +def add_absent_count_to_records(records): + """为考勤记录列表添加缺勤次数计算属性""" + total_absent_count = 0 + for record in records: + absent_count = calculate_absent_count_for_record(record) + record.absent_count = absent_count + total_absent_count += absent_count + return total_absent_count + + student_bp = Blueprint('student', __name__) @@ -35,9 +97,9 @@ def dashboard(): db.func.sum(WeeklyAttendance.actual_work_hours) ).filter_by(student_number=current_user.student_number).scalar() or 0 - total_absent_days = db.session.query( - db.func.sum(WeeklyAttendance.absent_days) - ).filter_by(student_number=current_user.student_number).scalar() or 0 + # 获取所有考勤记录并计算真实缺勤次数 + all_records = WeeklyAttendance.query.filter_by(student_number=current_user.student_number).all() + total_absent_count = add_absent_count_to_records(all_records) # 获取未审批的请假记录 pending_leaves = LeaveRecord.query.filter_by( @@ -50,7 +112,7 @@ def dashboard(): recent_attendance=recent_attendance, total_records=total_records, total_work_hours=float(total_work_hours), - total_absent_days=int(total_absent_days), + total_absent_count=int(total_absent_count), pending_leaves=pending_leaves) @@ -147,7 +209,9 @@ def attendance(): total_actual_hours = sum(record.actual_work_hours for record, _ in all_records) total_class_hours = sum(record.class_work_hours for record, _ in all_records) - total_absent_days = sum(record.absent_days for record, _ in all_records) + # 为记录添加缺勤次数计算 + record_list = [record for record, _ in all_records] + total_absent_count = add_absent_count_to_records(record_list) total_overtime_hours = sum(record.overtime_hours for record, _ in all_records) total_late_count = sum(late_count for _, late_count in all_records) @@ -164,7 +228,7 @@ def attendance(): 'total_weeks': len(all_records), 'total_actual_hours': total_actual_hours, 'total_class_hours': total_class_hours, - 'total_absent_days': total_absent_days, + 'total_absent_count': total_absent_count, 'total_overtime_hours': total_overtime_hours, 'total_late_count': total_late_count, 'total_leave_days': total_leave_days, @@ -200,6 +264,61 @@ def attendance_details(record_id): weekly_record_id=record_id ).order_by(DailyAttendanceDetail.attendance_date).all() + # 🔥 新增:计算真实的缺勤次数和迟到次数 + def calculate_real_counts(daily_details_list): + """计算真实的缺勤次数和迟到次数""" + late_count = 0 + absent_count = 0 + + for detail in daily_details_list: + # 🔥 处理完全缺勤的情况 + if detail.status == '缺勤' and (not detail.remarks or not detail.remarks.startswith('{')): + # 完全缺勤的天数,早上+下午都缺勤 + absent_count += 2 # 早上缺勤1次 + 下午缺勤1次 + continue + + if detail.remarks and detail.remarks.startswith('{'): + try: + remarks_data = json.loads(detail.remarks) + details_info = remarks_data.get('details', {}) + + # 统计迟到次数 + for period in ['morning', 'afternoon', 'evening']: + period_data = details_info.get(period, {}) + if period_data.get('status') == 'late': + late_count += 1 + + # 统计缺勤次数 + # 检查早上缺勤:morning_in AND morning_out 都missing + morning_data = details_info.get('morning', {}) + morning_in_time = morning_data.get('in') + morning_out_time = morning_data.get('out') + morning_in_status = morning_data.get('status', 'missing') + + if ((not morning_in_time or morning_in_status == 'missing') and + (not morning_out_time)): + absent_count += 1 + + # 检查下午缺勤:afternoon_in AND afternoon_out 都missing + afternoon_data = details_info.get('afternoon', {}) + afternoon_in_time = afternoon_data.get('in') + afternoon_out_time = afternoon_data.get('out') + afternoon_in_status = afternoon_data.get('status', 'missing') + + if ((not afternoon_in_time or afternoon_in_status == 'missing') and + (not afternoon_out_time)): + absent_count += 1 + + except (json.JSONDecodeError, KeyError, AttributeError): + continue + + return late_count, absent_count + + # 🔥 计算真实的次数并添加到record对象中 + real_late_count, real_absent_count = calculate_real_counts(daily_details) + record.late_count = real_late_count + record.absent_count = real_absent_count + # 处理每日详情,计算工作时长和解析详细信息 processed_daily_details = [] for detail in daily_details: @@ -264,8 +383,9 @@ def attendance_details(record_id): desc(WeeklyAttendance.week_start_date) ).limit(5).all() + # 🔥 修改:传递 weekly_record 而不是 record,以匹配模板 return render_template('student/attendance_details.html', - record=record, + weekly_record=record, # 🔥 关键修改 student=student, daily_details=processed_daily_details, total_days=total_days, @@ -275,7 +395,6 @@ def attendance_details(record_id): avg_daily_hours=avg_daily_hours, recent_records=recent_records) - @student_bp.route('/statistics') @login_required def statistics(): @@ -313,12 +432,15 @@ def statistics(): # 获取考勤记录,按周排序 attendance_records = attendance_query.order_by(desc(WeeklyAttendance.week_start_date)).all() + # 计算真实缺勤次数 + add_absent_count_to_records(attendance_records) + # 计算统计数据 total_stats = { 'total_work_hours': sum(record.actual_work_hours for record in attendance_records), 'total_class_hours': sum(record.class_work_hours for record in attendance_records), 'total_overtime_hours': sum(record.overtime_hours for record in attendance_records), - 'total_absent_days': sum(record.absent_days for record in attendance_records), + 'total_absent_count': sum(getattr(record, 'absent_count', 0) for record in attendance_records), 'attendance_weeks': len(attendance_records) } @@ -400,11 +522,15 @@ def statistics(): if all_records: # 计算入学以来的总统计 enrollment_weeks = (datetime.now().date() - student.enrollment_date).days // 7 + + # 🔥 修复:先计算缺勤次数,再定义字典 + add_absent_count_to_records(all_records) + all_time_stats = { 'total_work_hours': sum(record.actual_work_hours for record in all_records), 'total_class_hours': sum(record.class_work_hours for record in all_records), 'total_overtime_hours': sum(record.overtime_hours for record in all_records), - 'total_absent_days': sum(record.absent_days for record in all_records), + 'total_absent_count': sum(getattr(record, 'absent_count', 0) for record in all_records), 'attendance_weeks': len(all_records), 'enrollment_weeks': enrollment_weeks, 'attendance_rate': round(len(all_records) / max(enrollment_weeks, 1) * 100, diff --git a/app/templates/student/attendance.html b/app/templates/student/attendance.html index 77a4c19..69867ca 100644 --- a/app/templates/student/attendance.html +++ b/app/templates/student/attendance.html @@ -108,10 +108,10 @@