从青涩到 AI:我与评估程序的三十年 “纠缠” 与重启(参数化)

接上篇:从青涩到 AI:我与评估程序的三十年 “纠缠” 与重启
  主要对参数配置和模板文件处理进行了改动,将可参数化的数据放到了config.yaml文件中,再一个将模板文件(评估模板.xlsx)分离为(7年级模板.xlsx,8年级模板.xlsx,9年级模板.xlsx),真正做到评估结果直接可以打印。对于各年级评估算法一样,只是科目名不同,所以模板大同小异,分别设置模板后,以免打印时再次重新调整,提高易用性和效率。

主要改动:

  1. 评估规则参数化配置:

    • 核心改动: 将之前硬编码在 Python 代码 (score_evaluator.py_evaluate_* 函数中) 的大量评估规则数值移到了 config.yaml 文件中。
    • 具体参数: 这包括:
      • 排名阈值: 如一层次评估中的 80 名、160 名界限。
      • 重点评估得分: 每个项目中,符合条件的学生所得的分数(例如,一层次班团总分前 80 名得 2 分,二层次教师单科优秀且总分合格得 2 分等)。
      • 全面评估权重/乘数: 在计算优秀率、平均分、合格率、非低分率等指标相对于同层次最高值的得分时,所使用的乘数(例如,一层次班团优秀率积分乘以 40)。
      • 教师最终总分权重: 在计算教师最终总分时,小计1 和小计2 所占的百分比(例如,一层次教师的小计1权重为 0.3,小计2权重为 0.7)。
    • 实现方式: 在 config.yaml 的每个年级配置下增加了 evaluation_rules 部分,并细分了 class_teamteacher,再按 level_1level_2 组织这些参数。Python 代码中的相应评估函数被修改为从 self.eval_rules 读取这些配置值。
  2. 模板文件按年级配置:

    • 核心改动: 将原先在 config.yamlgeneral_settings 中的全局 template_file 设置,移至每个年级(7, 8, 9)各自的配置块内。
    • 实现方式: 每个年级配置下现在都有一个独立的 template_file 键,例如 7: template_file: '7年级模板.xlsx'。Python 代码 (__init__, check_required_files, save_results) 被修改为读取和使用当前评估年级对应的 template_file 配置。
  3. 代码适应性修改:

    • ScoreEvaluator.__init__: 更新以加载新的 evaluation_rules 和年级特定的 template_file
    • load_config: 更新以验证新的配置结构。
    • 评估函数 (_evaluate_*): 更新以使用从配置加载的规则参数。
    • 文件处理函数 (check_required_files, save_results): 更新以使用年级特定的模板文件名。
  4. 计算逻辑修正 (附带):

    • 根据您的确认,修正了 _evaluate_second_level_class 函数中二层次班团的总积分计算逻辑,使其现在等于未加权的小计1与未加权的小计2之和。

这些改动带来的好处:

  1. 提高灵活性和可配置性:

    • 最显著的好处是,如果学校将来需要调整评估标准(例如修改排名界限、调整各项得分或权重),只需修改 config.yaml 文件即可,不再需要深入 Python 代码进行修改。这大大降低了调整评估规则的难度和风险。
    • 可以轻松地为不同年级设置不同的评估规则和模板文件,满足差异化需求。
  2. 增强可维护性:

    • 将易变的评估规则与相对稳定的计算逻辑分离。代码 (.py) 专注于“如何计算”,配置文件 (.yaml) 则定义“计算标准是什么”。
    • 规则集中存放在 config.yaml 中,更易于查找、理解和管理。修改配置通常比修改代码更安全,减少了引入程序错误的可能性。
  3. 提升代码清晰度和可读性:

    • 评估函数中的代码不再充斥着各种“魔法数字”,而是引用具有明确含义的配置变量(如 rules['focus_points_rank1']),使得代码逻辑更易懂。
  4. 便于扩展和重用:

    • 如果需要为新的年级(如六年级)添加评估,只需在 config.yaml 中增加对应的配置块,并准备好相应的模板文件,代码主体基本无需改动。
    • 这套程序更容易被其他需要类似评估逻辑的场景复用,只需修改配置文件即可适应新规则。

总而言之,这些改动将评估的核心“规则”部分从程序代码中解耦出来,放入了专门的配置文件,使得程序更加灵活、易于维护和适应未来的变化。

修改后配置文件 “config.yaml”

# === 程序配置文件 ===

# --- 通用设置 ---
general_settings:
  # template_file: '评估模板.xlsx'      # <--- 移除此行
  # !!! 重要:请根据实际教师分工表修改此项 !!!
  head_teacher_column_name: '班主任'  # 教师分工表中代表"班主任"的列名 
  class_team_output_label: '班团'    # 在最终评估结果中代表"班团"评估的标签名

# --- 评分标准阈值 (百分比) ---
score_thresholds:
  excellent_rate: 0.8  # 优秀线比例 (满分的80%)
  pass_rate: 0.6       # 合格线比例 (满分的60%)
  low_score_rate: 0.3  # 低分线低于此比例 (满分的30%)

# --- 年级特定配置 ---
grades:
  # --- 七年级配置 ---
  7: 
    template_file: '7年级模板.xlsx'  # <--- 新增七年级模板配置
    # 成绩表科目列名 (参与总分计算和排名)
    subjects: 
      - 语文
      - 数学
      - 英语
      - 道法
      - 历史
      - 地理
      - 生物
    # 各科目满分
    full_scores:       
      语文: 120
      数学: 120
      英语: 90
      道法: 60
      历史: 60
      地理: 50
      生物: 100
    # 教师分工表科目列名 (程序将查找这些列以获取教师姓名)
    teacher_file_subjects: 
      - 语文
      - 数学
      - 英语
      - 生物
      - 地理
      - 道法
      - 历史
    # --- 评估规则 ---
    evaluation_rules:
      class_team: # 班团评估
        level_1:  # 一层次
          rank_threshold_1: 80         # 重点评估项目1排名界限
          rank_threshold_2: 160        # 重点评估项目2排名界限
          focus_points_rank1: 2        # 项目1得分/人
          focus_points_rank2: 1.5      # 项目2得分/人
          focus_points_excellent: 1    # 项目3得分/人
          comp_weight_excellent: 40    # 全面评估-优生率积分权重
          comp_weight_avg: 40          # 全面评估-平均分积分权重
          comp_weight_non_low: 20      # 全面评估-非低分率积分权重
        level_2:  # 二层次
          focus_points_excellent: 2    # 项目1得分/人
          focus_points_pass: 1.5       # 项目2得分/人
          comp_weight_excellent: 5     # 全面评估-优生率积分权重
          comp_weight_pass: 30         # 全面评估-合格率积分权重
          comp_weight_avg: 30          # 全面评估-平均分积分权重
          comp_weight_non_low: 35      # 全面评估-非低分率积分权重
          # 注意:二层次班团总分 = 小计1 + 小计2 (直接相加)
      teacher: # 教师评估
        level_1: # 一层次
          rank_threshold_1: 80         # 重点评估项目1&2排名界限
          rank_threshold_2: 160
          focus_points_p1: 2           # 项目1得分/人
          focus_points_p2: 1.5         # 项目2得分/人
          focus_points_p3: 1           # 项目3得分/人
          focus_points_p4: 0.5         # 项目4得分/人
          comp_weight_excellent: 40    # 全面评估-优生率积分权重
          comp_weight_avg: 40          # 全面评估-平均分积分权重
          comp_weight_non_low: 20      # 全面评估-非低分率积分权重
          final_weight_sub1: 0.3       # 最终总分中小计1权重
          final_weight_sub2: 0.7       # 最终总分中小计2权重
        level_2: # 二层次
          focus_points_p1: 2           # 项目1得分/人
          focus_points_p2: 1.5         # 项目2得分/人
          comp_weight_excellent: 5     # 全面评估-优生率积分权重
          comp_weight_pass: 30         # 全面评估-合格率积分权重
          comp_weight_avg: 30          # 全面评估-平均分积分权重
          comp_weight_non_low: 35      # 全面评估-非低分率积分权重
          final_weight_sub1: 0.2       # 最终总分中小计1权重
          final_weight_sub2: 0.8       # 最终总分中小计2权重

  # --- 八年级配置 ---
  8: 
    template_file: '8年级模板.xlsx'  # <--- 新增八年级模板配置
    subjects: 
      - 语文
      - 数学
      - 英语
      - 物理
      - 生物
      - 地理
      - 历史
      - 道法
    full_scores:
      语文: 120
      数学: 120
      英语: 120
      物理: 70
      生物: 50
      地理: 50
      历史: 60
      道法: 60
    teacher_file_subjects:
      - 语文
      - 数学
      - 英语
      - 物理
      - 道法
      - 历史
      - 地理
      - 生物
    # --- 评估规则 ---
    evaluation_rules: # (与7年级相同,如果不同请修改)
      class_team: 
        level_1: 
          rank_threshold_1: 80
          rank_threshold_2: 160
          focus_points_rank1: 2
          focus_points_rank2: 1.5
          focus_points_excellent: 1
          comp_weight_excellent: 40
          comp_weight_avg: 40
          comp_weight_non_low: 20
        level_2: 
          focus_points_excellent: 2
          focus_points_pass: 1.5
          comp_weight_excellent: 5
          comp_weight_pass: 30
          comp_weight_avg: 30
          comp_weight_non_low: 35
      teacher: 
        level_1: 
          rank_threshold_1: 80
          rank_threshold_2: 160
          focus_points_p1: 2
          focus_points_p2: 1.5
          focus_points_p3: 1
          focus_points_p4: 0.5
          comp_weight_excellent: 40
          comp_weight_avg: 40
          comp_weight_non_low: 20
          final_weight_sub1: 0.3
          final_weight_sub2: 0.7
        level_2: 
          focus_points_p1: 2
          focus_points_p2: 1.5
          comp_weight_excellent: 5
          comp_weight_pass: 30
          comp_weight_avg: 30
          comp_weight_non_low: 35
          final_weight_sub1: 0.2
          final_weight_sub2: 0.8
              
  # --- 九年级配置 ---
  9: 
    template_file: '9年级模板.xlsx'  # <--- 新增九年级模板配置
    subjects: 
      - 语文
      - 数学
      - 英语
      - 物理
      - 化学
      - 历史
      - 道法
    full_scores:
      语文: 120
      数学: 120
      英语: 120
      物理: 80
      化学: 70
      历史: 50
      道法: 50
    teacher_file_subjects:
      - 语文
      - 数学
      - 英语
      - 物理
      - 化学
      - 历史
      - 道法
    # --- 评估规则 ---
    evaluation_rules: # (与7年级相同,如果不同请修改)
      class_team: 
        level_1: 
          rank_threshold_1: 80
          rank_threshold_2: 160
          focus_points_rank1: 2
          focus_points_rank2: 1.5
          focus_points_excellent: 1
          comp_weight_excellent: 40
          comp_weight_avg: 40
          comp_weight_non_low: 20
        level_2: 
          focus_points_excellent: 2
          focus_points_pass: 1.5
          comp_weight_excellent: 5
          comp_weight_pass: 30
          comp_weight_avg: 30
          comp_weight_non_low: 35
      teacher: 
        level_1: 
          rank_threshold_1: 80
          rank_threshold_2: 160
          focus_points_p1: 2
          focus_points_p2: 1.5
          focus_points_p3: 1
          focus_points_p4: 0.5
          comp_weight_excellent: 40
          comp_weight_avg: 40
          comp_weight_non_low: 20
          final_weight_sub1: 0.3
          final_weight_sub2: 0.7
        level_2: 
          focus_points_p1: 2
          focus_points_p2: 1.5
          comp_weight_excellent: 5
          comp_weight_pass: 30
          comp_weight_avg: 30
          comp_weight_non_low: 35
          final_weight_sub1: 0.2
          final_weight_sub2: 0.8

修改后的程序

import pandas as pd
import numpy as np
import os
import shutil
from datetime import datetime
import yaml # 新增:用于读取 YAML 配置

# 尝试导入 openpyxl,如果失败则提示安装
try:
    from openpyxl import load_workbook
except ImportError:
    print("错误:缺少必需的库 'openpyxl'。请使用 'pip install openpyxl' 命令安装后再试。")
    raise # 重新抛出异常

# --- 全局配置变量 --- 
CONFIG = None # 用于存储加载的配置
CONFIG_FILE = 'config.yaml' # 配置文件名

def load_config(config_path=CONFIG_FILE):
    """加载 YAML 配置文件"""
    global CONFIG
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            CONFIG = yaml.safe_load(f)
        if CONFIG is None:
            raise ValueError(f"配置文件 '{config_path}' 为空或格式不正确。")
        # 基本的配置验证 (可以根据需要扩展)
        if 'general_settings' not in CONFIG or 'score_thresholds' not in CONFIG or 'grades' not in CONFIG:
             raise ValueError(f"配置文件 '{config_path}' 缺少必需的顶层键 (general_settings, score_thresholds, grades)。")
        # !! 新增: 检查每个年级配置中是否有 template_file 和 evaluation_rules
        for grade, grade_config in CONFIG['grades'].items():
            if 'template_file' not in grade_config:
                 raise ValueError(f"配置文件 '{config_path}' 中年级 {grade} 缺少必需的 'template_file' 配置。")
            if 'evaluation_rules' not in grade_config:
                raise ValueError(f"配置文件 '{config_path}' 中年级 {grade} 缺少必需的 'evaluation_rules' 配置。")
            # 可选:更深入地检查 evaluation_rules 内部结构
            if 'class_team' not in grade_config['evaluation_rules'] or 'teacher' not in grade_config['evaluation_rules']:
                 raise ValueError(f"配置文件 '{config_path}' 中年级 {grade} 的 'evaluation_rules' 缺少 'class_team' 或 'teacher' 配置。")
            # ... 可根据需要添加更多层级的检查 ...
        print(f"配置文件 '{config_path}' 加载成功。")
    except FileNotFoundError:
        print(f"错误:配置文件 '{config_path}' 未找到。请确保它与脚本在同一目录下。")
        raise
    except yaml.YAMLError as e:
        print(f"错误:解析配置文件 '{config_path}' 时出错:{e}")
        raise
    except ValueError as e: # 捕获上面添加的 ValueError
        print(f"错误:配置文件 '{config_path}' 内容不完整或格式错误:{e}")
        raise
    except Exception as e:
        print(f"加载配置文件时发生未知错误:{e}")
        raise

class ScoreEvaluator:
    def __init__(self, grade):
        global CONFIG
        if CONFIG is None:
            # 尝试加载配置,如果 main 函数未预先加载
            try:
                load_config()
            except Exception:
                 print("在 ScoreEvaluator 初始化时加载配置失败。程序无法继续。")
                 raise RuntimeError("无法加载配置文件") 

        # grade 现在是整数
        if grade not in CONFIG['grades']: 
            supported_grades_str = [str(k) for k in CONFIG['grades'].keys()]
            raise ValueError(f"不支持的年级: {grade}. 配置文件中支持的年级: {supported_grades_str}")
        
        self.grade = grade 
        self.grade_config = CONFIG['grades'][grade] 
        self.general_config = CONFIG['general_settings']
        self.threshold_config = CONFIG['score_thresholds']
        
        # --- 关键修复:确保加载评估规则 ---
        # 检查 evaluation_rules 是否存在,如果不存在则抛出更明确的错误
        if 'evaluation_rules' not in self.grade_config:
             raise ValueError(f"配置文件 '{CONFIG_FILE}' 中年级 {grade} 缺少必需的 'evaluation_rules' 配置。")
        self.eval_rules = self.grade_config['evaluation_rules'] 
        # -------------------------------------

        # --- 从配置加载其他设置 ---
        self.SUBJECTS = self.grade_config['subjects']
        self.SUBJECT_FULL_SCORES = self.grade_config['full_scores']
        self.TEACHER_FILE_SUBJECTS = self.grade_config['teacher_file_subjects']
        # !! 修改: 从年级配置加载 template_file
        if 'template_file' not in self.grade_config:
             raise ValueError(f"配置文件 '{CONFIG_FILE}' 中年级 {grade} 缺少必需的 'template_file' 配置。")
        self.template_file = self.grade_config['template_file'] 
        self.head_teacher_col = self.general_config['head_teacher_column_name']
        self.class_team_label = self.general_config['class_team_output_label']
        self.SCORE_THRESHOLDS = self.threshold_config
        # -----------------------

        # --- 设置动态文件名和目录名 ---
        self.scores_file = f'{self.grade}年级成绩表.xlsx'
        self.teachers_file = f'{self.grade}年级教师分工表.xlsx'
        self.detailed_data_dir = f'{self.grade}年级详细数据'
        # ---------------------------
        
        self.subject_thresholds = self._calculate_subject_thresholds()
        
        if not os.path.exists(self.detailed_data_dir):
            os.makedirs(self.detailed_data_dir)
        
    def _calculate_subject_thresholds(self):
        """计算各科目的优秀、合格、低分线"""
        thresholds = {}
        # 使用 self.SUBJECT_FULL_SCORES 和 self.SCORE_THRESHOLDS (从配置加载)
        for subject, full_score in self.SUBJECT_FULL_SCORES.items():
            thresholds[subject] = {
                'excellent': full_score * self.SCORE_THRESHOLDS['excellent_rate'],
                'pass': full_score * self.SCORE_THRESHOLDS['pass_rate'],
                'low': full_score * self.SCORE_THRESHOLDS['low_score_rate']
            }
        return thresholds

    def check_required_files(self):
        """检查必需的文件是否存在"""
        # 使用动态和配置的文件名
        required_files = [self.scores_file, self.teachers_file, self.template_file]
        missing_files = []
        
        for file in required_files:
            if not os.path.exists(file):
                missing_files.append(file)
        
        if missing_files:
            # !! 修改: 明确指出哪个模板文件缺失
            if self.template_file in missing_files:
                 raise FileNotFoundError(f"缺少必需文件,特别是 {self.grade} 年级的模板文件 '{self.template_file}'。其他缺失文件(如果有):{', '.join(f for f in missing_files if f != self.template_file)}")
            else:
                 raise FileNotFoundError(f"缺少以下必需文件:{', '.join(missing_files)}")
        
    def prepare_data(self):
        """准备评估所需的数据"""
        self.check_required_files()
        
        try:
            # 读取原始数据 (使用动态文件名)
            self.scores_df = pd.read_excel(self.scores_file)
            self.teachers_df = pd.read_excel(self.teachers_file)
            # self.template_df = pd.read_excel(self.template_file) # 不再需要读取模板内容
            
            # 检查必需的列 (使用配置的科目列表和班主任列名)
            required_scores_columns = ['评估班级', '层次', '准考证', '姓名'] + self.SUBJECTS # 确保准考证和姓名也在检查列表里
            # 教师表需要班号、配置的班主任列名、以及配置的所有科目教师列
            required_teachers_columns = ['班号', self.head_teacher_col] + self.TEACHER_FILE_SUBJECTS
            
            missing_scores_cols = [col for col in required_scores_columns if col not in self.scores_df.columns]
            if missing_scores_cols:
                raise ValueError(f"{self.scores_file} 缺少必需的列:{', '.join(missing_scores_cols)}")
            
            missing_teachers_cols = [col for col in required_teachers_columns if col not in self.teachers_df.columns]
            if missing_teachers_cols:
                raise ValueError(f"{self.teachers_file} 缺少必需的列:{', '.join(missing_teachers_cols)}")

            # 验证层次数据 (保持不变)
            if not self.scores_df['层次'].isin(['一层次', '二层次']).all():
                raise ValueError(f"{self.scores_file} 中的层次列必须只包含'一层次'或'二层次'")
            
            # 将层次值映射为数字 (保持不变)
            self.scores_df['层次_数值'] = self.scores_df['层次'].map({'一层次': 1, '二层次': 2})
            
            # 重新组织教师数据 (使用配置的班主任列名和科目列表)
            teacher_data = []
            for _, row in self.teachers_df.iterrows():
                班号 = row['班号']
                # 添加班主任记录 (使用配置的列名)
                teacher_data.append({
                    '班号': 班号,
                    '教师姓名': row[self.head_teacher_col],
                    '学科': '__HeadTeacher__' # 使用内部固定标识符,不暴露给配置
                })
                # 添加各科目教师记录 (使用配置的 self.TEACHER_FILE_SUBJECTS)
                for subject in self.TEACHER_FILE_SUBJECTS:
                    teacher_data.append({
                        '班号': 班号,
                        '教师姓名': row[subject],
                        '学科': subject
                    })
            
            # 转换为DataFrame
            self.teachers_df = pd.DataFrame(teacher_data)
            
            # 创建班主任映射字典 (使用内部标识符查找)
            self.class_teacher_map = self.teachers_df[self.teachers_df['学科'] == '__HeadTeacher__'].set_index('班号')['教师姓名'].to_dict()
            
            # 计算总分 (使用配置的科目列表 self.SUBJECTS)
            self.scores_df['总分'] = self.scores_df[self.SUBJECTS].sum(axis=1)
            
            # 计算各科目排名
            self._calculate_rankings()
            
        except KeyError as e:
             # 捕获因配置错误或Excel列名不匹配导致的KeyError
             print(f"数据准备过程中出现列名错误:找不到列 '{e}'。")
             print(f"请检查配置文件 ({CONFIG_FILE}) 中的科目/班主任列名是否与 Excel 文件 ({self.scores_file}, {self.teachers_file}) 完全匹配。")
             raise ValueError(f"列名不匹配错误: {e}")
        except Exception as e:
            print(f"数据准备过程中出错:{str(e)}") # 提供更具体的错误信息
            raise

    def _calculate_rankings(self):
        """计算各科目的排名"""
        # 使用配置的科目列表 self.SUBJECTS
        subjects_to_rank = self.SUBJECTS + ['总分']
        
        # 计算全校排名 (保持不变)
        for subject in subjects_to_rank:
             if subject in self.scores_df.columns:
                self.scores_df[f'{subject}_全校名次'] = self.scores_df[subject].rank(ascending=False, method='min')
            
        # 计算班级排名 (保持不变)
        for subject in subjects_to_rank:
            if subject in self.scores_df.columns:
                 self.scores_df[f'{subject}_班级名次'] = self.scores_df.groupby('评估班级')[subject].rank(ascending=False, method='min')

    def _safe_max(self, values, default=0):
        """安全地计算最大值,如果列表为空则返回默认值"""
        try:
            # 过滤掉可能的NaN值或其他非数值类型
            numeric_values = [v for v in values if isinstance(v, (int, float)) and not np.isnan(v)]
            return max(numeric_values) if numeric_values else default
        except Exception:
            # 更广泛的异常捕获可能更安全
            return default

    def _save_detailed_data(self, class_data, class_num, subject, level, debug_info):
        """保存详细评估数据到文件"""
        # 检查输出的科目名是否是内部的班主任标记,如果是,则替换为配置的输出标签
        output_subject_name = self.class_team_label if subject == '__HeadTeacher__' else subject
        try:
            # 使用动态目录名和处理过的科目名
            filename = os.path.join(self.detailed_data_dir, f'{output_subject_name}.txt')

            # 以追加模式打开文件
            with open(filename, 'a', encoding='utf-8') as f:
                f.write(f"\n{'='*50}")
                f.write(f"\n{class_num}班({level})详细数据 - 科目: {output_subject_name}\n") # 明确指出科目

                # 添加所有考生基本信息
                f.write(f"\n--- 所有考生基本信息 (总人数: {len(class_data)}) ---\n")
                for _, student in class_data.iterrows():
                    # 如果是班团评估(用内部标记判断),只显示总分相关
                    if subject == '__HeadTeacher__': 
                        f.write(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, 总分:{student['总分']}, 总分名次:{student['总分_全校名次']}\n")
                    else:
                        # 对于其他科目,显示科目相关信息 (保持不变)
                        subject_score = student.get(subject, 'N/A')
                        subject_rank = student.get(f'{subject}_全校名次', 'N/A')
                        total_score = student.get('总分', 'N/A')
                        total_rank = student.get('总分_全校名次', 'N/A')
                        f.write(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, {subject}{subject_score}, {subject}名次:{subject_rank}, 总分:{total_score}, 总分名次:{total_rank}\n")

                # --- 解析和写入 debug_info 的逻辑保持不变 --- 
                project_sections = []
                assessment_data = []
                assessment_calc = []
                current_section = None
                section_type = None # 可以是 'project', 'data', 'calc'

                for line in debug_info:
                    line_stripped = line.strip()

                    # 跳过空行或不必要的行
                    if not line_stripped:
                        continue

                    # 检测部分标题
                    if line_stripped.startswith("--- 项目") and line_stripped.endswith("---"):
                        if current_section and section_type == 'project':
                            project_sections.append(current_section)
                        current_section = {"title": line_stripped, "description": [], "students": [], "summary": None}
                        section_type = 'project'
                        continue
                    elif line_stripped.startswith("--- 全面评估数据 ---"):
                        if current_section and section_type == 'project': # 完成最后一个项目
                             project_sections.append(current_section)
                        current_section = None
                        section_type = 'data'
                        continue
                    elif line_stripped.startswith("--- 全面评估积分计算 ---"):
                        section_type = 'calc'
                        continue
                    # 其他类型的分隔符可以视为部分的结束
                    elif line_stripped.startswith("---"):
                        if current_section and section_type == 'project':
                            project_sections.append(current_section)
                        current_section = None
                        section_type = None # 重置类型
                        continue

                    # 根据当前部分类型收集内容
                    if section_type == 'project' and current_section:
                        if "准考证" in line and "姓名" in line:
                            current_section["students"].append(line_stripped)
                        elif "线" in line or "范围" in line:
                            if not ("人数" in line and "得分" in line):
                                current_section["description"].append(line_stripped)
                        elif "项目" in line and "人数" in line and "得分" in line:
                            current_section["summary"] = line_stripped
                    elif section_type == 'data':
                        assessment_data.append(line_stripped)
                    elif section_type == 'calc':
                        assessment_calc.append(line_stripped)

                # 确保最后一个项目(如果存在)被添加
                if current_section and section_type == 'project':
                    project_sections.append(current_section)
                # --- 结束修改后的解析逻辑 ---

                # 写入记分项目考生详细信息部分
                f.write("\n--- 记分项目考生详细信息 ---\n")
                if not project_sections:
                     f.write("无记分项目信息\n")
                else:
                    for section in project_sections:
                        # 写入项目标题
                        f.write(f"\n{section['title']}\n")
                        # 写入项目描述
                        for desc in section["description"]:
                            f.write(f"{desc}\n")
                        # 写入考生信息
                        if section["students"]:
                            f.write("考生详细信息:\n")
                            for student in section["students"]:
                                f.write(f"{student}\n")
                        else:
                            f.write("无符合条件的考生\n")
                        # 写入项目汇总
                        if section["summary"]:
                            f.write(f"{section['summary']}\n")

                # 写入全面评估数据
                f.write("\n--- 全面评估数据 ---\n")
                if not assessment_data:
                    f.write("无全面评估数据\n")
                else:
                    for line in assessment_data:
                         f.write(f"{line}\n")

                # 写入全面评估积分计算
                f.write("\n--- 全面评估积分计算 ---\n")
                if not assessment_calc:
                     f.write("无全面评估积分计算数据\n")
                else:
                    for line in assessment_calc:
                         f.write(f"{line}\n")

                f.write('\n')
        except Exception as e:
            print(f"保存科目 '{output_subject_name}' 的详细数据时出错:{str(e)}") # 提示哪个科目出错
            # 不再向上抛出,避免一个科目的错误中断整个流程,但会在最后提示
            # raise 

    def _evaluate_first_level_class(self, class_data, class_num):
        """一层次班级评估"""
        total_students = len(class_data)
        debug_info = []  # 用于存储调试信息
        
        # 创建一个标记数组,用于记录已计分的学生
        counted_students = pd.Series(False, index=class_data.index)
        
        # --- 重点评估 --- 
        # 项目1:总分1-80名(2分)
        top_80_mask = class_data['总分_全校名次'] <= 80
        top_80_students = class_data[top_80_mask]
        top_80_count = len(top_80_students) # 使用len确保一致性
        score_1 = top_80_count * 2
        counted_students = counted_students | top_80_mask
        
        debug_info.append(f"\n总人数:{total_students}") # 移到项目1之前
        debug_info.append("\n--- 项目1:总分1-80名(2分)---")
        for _, student in top_80_students.iterrows():
            debug_info.append(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, 总分:{student['总分']}, 总分名次:{student['总分_全校名次']}")
        debug_info.append(f"项目1人数:{top_80_count}, 得分:{score_1}")
        
        # 项目2:总分81-160名(1.5分)
        top_81_160_mask_scored = (class_data['总分_全校名次'] > 80) & (class_data['总分_全校名次'] <= 160) & ~counted_students
        top_81_160_students_scored = class_data[top_81_160_mask_scored]
        top_81_160_count = len(top_81_160_students_scored)
        score_2 = top_81_160_count * 1.5
        counted_students = counted_students | top_81_160_mask_scored
        
        debug_info.append("\n--- 项目2:总分81-160名(1.5分)---")
        debug_info.append(f"总分名次范围:81-160名")
        for _, student in top_81_160_students_scored.iterrows():
            debug_info.append(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, 总分:{student['总分']}, 总分名次:{student['总分_全校名次']}")
        debug_info.append(f"项目2人数:{top_81_160_count}, 得分:{score_2}")
        
        # 项目3:总分优秀(1分)
        # 使用动态科目列表计算总分优秀线
        excellent_threshold = sum([self.subject_thresholds[s]['excellent'] for s in self.SUBJECTS if s in self.subject_thresholds])
        excellent_mask_base = class_data['总分'] >= excellent_threshold
        excellent_mask_scored = excellent_mask_base & ~counted_students
        excellent_students_scored = class_data[excellent_mask_scored]
        excellent_count = len(excellent_students_scored)
        score_3 = excellent_count * 1
        counted_students = counted_students | excellent_mask_scored

        debug_info.append("\n--- 项目3:总分优秀(1分)---")
        debug_info.append(f"总分优秀线:{excellent_threshold}") # 修改名称
        for _, student in excellent_students_scored.iterrows():
            debug_info.append(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, 总分:{student['总分']}, 总分名次:{student['总分_全校名次']}")
        debug_info.append(f"项目3人数:{excellent_count}, 得分:{score_3}")
        
        # --- 全面评估 --- 
        excellent_rate = sum(excellent_mask_base) / total_students # 使用基础优秀标记
        # 使用动态科目列表计算总分合格线
        pass_threshold = sum([self.subject_thresholds[s]['pass'] for s in self.SUBJECTS if s in self.subject_thresholds])
        pass_mask_base = class_data['总分'] >= pass_threshold # 使用基础合格标记
        pass_rate = sum(pass_mask_base) / total_students # 使用基础合格标记
        avg_score = class_data['总分'].mean()
        
        # 使用动态科目列表计算总分低分线
        low_threshold = sum([self.subject_thresholds[s]['low'] for s in self.SUBJECTS if s in self.subject_thresholds])
        low_score_mask = class_data['总分'] < low_threshold
        non_low_score_rate = 1 - (sum(low_score_mask) / total_students)
        low_score_count = sum(low_score_mask)
        
        debug_info.append("\n--- 全面评估数据 ---")
        debug_info.append(f"总分优秀线:{excellent_threshold}") # 修改名称
        debug_info.append(f"总分合格线:{pass_threshold}") # 修改名称
        debug_info.append(f"总分低分线:{low_threshold}") # 修改名称
        debug_info.append(f"优生数:{sum(excellent_mask_base)}") # 使用基础优秀标记
        debug_info.append(f"优生率:{excellent_rate*100:03.2f}%")
        debug_info.append(f"合格数:{sum(pass_mask_base)}") # 使用基础合格标记
        debug_info.append(f"合格率:{pass_rate*100:03.2f}%")
        debug_info.append(f"平均分:{avg_score:.2f}")
        debug_info.append(f"低分数:{low_score_count}")
        debug_info.append(f"非低分人数:{total_students - sum(low_score_mask)}")
        debug_info.append(f"非低分率:{non_low_score_rate*100:03.2f}%")
        
        # 计算相对值(与同层次班级比较)
        first_level_data = self.scores_df[self.scores_df['层次_数值'] == 1]
        if len(first_level_data) == 0:
            max_excellent_rate = 0
            max_avg_score = 0
            max_non_low_score_rate = 0
        else:
            # 在计算groupby后的指标时,确保分母不为0
            excellent_rates = [(sum(c['总分'] >= excellent_threshold) / len(c)) if len(c) > 0 else 0
                              for _, c in first_level_data.groupby('评估班级')]
            max_excellent_rate = self._safe_max(excellent_rates)
            
            avg_scores = [c['总分'].mean() if len(c) > 0 else 0 for _, c in first_level_data.groupby('评估班级')]
            max_avg_score = self._safe_max(avg_scores)
            
            non_low_score_rates = [(1 - sum(c['总分'] < low_threshold) / len(c)) if len(c) > 0 else 0
                                  for _, c in first_level_data.groupby('评估班级')]
            max_non_low_score_rate = self._safe_max(non_low_score_rates)
        
        # 计算全面评估各项积分
        excellent_rate_score = (excellent_rate/max_excellent_rate) * 40 if max_excellent_rate > 0 else 0
        avg_score_score = (avg_score/max_avg_score) * 40 if max_avg_score > 0 else 0
        non_low_rate_score = (non_low_score_rate/max_non_low_score_rate) * 20 if max_non_low_score_rate > 0 else 0
        
        debug_info.append("\n--- 全面评估积分计算 ---")
        debug_info.append(f"最高优生率:{max_excellent_rate*100:03.2f}%")
        debug_info.append(f"最高平均分:{max_avg_score:.2f}")
        debug_info.append(f"最高非低分率:{max_non_low_score_rate*100:03.2f}%")
        debug_info.append(f"优生率积分:{excellent_rate_score if excellent_rate_score.is_integer() else excellent_rate_score:.2f}")
        debug_info.append(f"平均分积分:{avg_score_score if avg_score_score.is_integer() else avg_score_score:.2f}")
        debug_info.append(f"非低分率积分:{non_low_rate_score if non_low_rate_score.is_integer() else non_low_rate_score:.2f}")
        
        # 保存详细数据
        self._save_detailed_data(class_data, class_num, '__HeadTeacher__', '一层次', debug_info)
        
        return {
            '班号': class_num,
            '班级人数': total_students,
            '教师姓名': self.class_teacher_map.get(class_num, ''),
            '层次': '一层次',
            '项目1': top_80_count,
            '积分1': score_1,
            '项目2': top_81_160_count,
            '积分2': score_2,
            '项目3': excellent_count,
            '积分3': score_3,
            '小计1': score_1 + score_2 + score_3,
            '优生数': sum(excellent_mask_base), # 使用基础优秀标记计数
            '优生率': excellent_rate,
            '优生积分': excellent_rate_score,
            '平均分': avg_score,
            '平均分积分': avg_score_score,
            '低分数': low_score_count,
            '非低分率': non_low_score_rate,
            '非低分率积分': non_low_rate_score,
            '小计2': excellent_rate_score + avg_score_score + non_low_rate_score,
            '总积分': (score_1 + score_2 + score_3) + (excellent_rate_score + avg_score_score + non_low_rate_score),
            '科目名': self.class_team_label # 使用配置的班团标签
        }

    def _evaluate_second_level_class(self, class_data, class_num):
        """二层次班级评估"""
        rules = self.eval_rules['class_team']['level_2'] # 获取规则
        total_students = len(class_data)
        debug_info = []
        
        # --- 重点评估 --- 
        # 项目1:总分优秀 (得分: focus_points_excellent)
        points_excellent = rules['focus_points_excellent']
        excellent_threshold = sum([self.subject_thresholds[s]['excellent'] for s in self.SUBJECTS if s in self.subject_thresholds])
        excellent_mask = class_data['总分'] >= excellent_threshold
        excellent_students = class_data[excellent_mask]
        excellent_count = len(excellent_students)
        score_1 = excellent_count * points_excellent
        
        debug_info.append(f"\n总人数:{total_students}")
        debug_info.append(f"\n--- 项目1:总分优秀({points_excellent}分)---")
        debug_info.append(f"总分优秀线:{excellent_threshold}")
        for _, student in excellent_students.iterrows():
            debug_info.append(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, 总分:{student['总分']}, 总分名次:{student['总分_全校名次']}")
        debug_info.append(f"项目1人数:{excellent_count}, 得分:{score_1}")
        
        # 项目2:总分合格 (得分: focus_points_pass)
        points_pass = rules['focus_points_pass']
        pass_threshold = sum([self.subject_thresholds[s]['pass'] for s in self.SUBJECTS if s in self.subject_thresholds])
        pass_mask_base = class_data['总分'] >= pass_threshold
        pass_mask_scored = pass_mask_base & ~excellent_mask # 合格且非优秀
        pass_students_scored = class_data[pass_mask_scored]
        pass_count = len(pass_students_scored)
        score_2 = pass_count * points_pass
        
        debug_info.append(f"\n--- 项目2:总分合格({points_pass}分)---")
        debug_info.append(f"总分合格线:{pass_threshold}")
        for _, student in pass_students_scored.iterrows():
            debug_info.append(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, 总分:{student['总分']}, 总分名次:{student['总分_全校名次']}")
        debug_info.append(f"项目2人数:{pass_count}, 得分:{score_2}")

        # 小计1 (未加权)
        subtotal_1 = score_1 + score_2
        
        # --- 全面评估 --- 
        excellent_rate = sum(excellent_mask) / total_students
        pass_rate = sum(pass_mask_base) / total_students
        avg_score = class_data['总分'].mean()
        low_threshold = sum([self.subject_thresholds[s]['low'] for s in self.SUBJECTS if s in self.subject_thresholds])
        low_score_mask = class_data['总分'] < low_threshold
        non_low_score_rate = 1 - (sum(low_score_mask) / total_students)
        low_score_count = sum(low_score_mask)
        
        debug_info.append("\n--- 全面评估数据 ---")
        debug_info.append(f"总分优秀线:{excellent_threshold}")
        debug_info.append(f"总分合格线:{pass_threshold}")
        debug_info.append(f"总分低分线:{low_threshold}")
        debug_info.append(f"优生数:{sum(excellent_mask)}")
        debug_info.append(f"优生率:{excellent_rate*100:03.2f}%")
        debug_info.append(f"合格数:{sum(pass_mask_base)}")
        debug_info.append(f"合格率:{pass_rate*100:03.2f}%")
        debug_info.append(f"平均分:{avg_score:.2f}")
        debug_info.append(f"低分数:{low_score_count}")
        debug_info.append(f"非低分人数:{total_students - sum(low_score_mask)}")
        debug_info.append(f"非低分率:{non_low_score_rate*100:03.2f}%")
        
        second_level_data = self.scores_df[self.scores_df['层次_数值'] == 2]
        if len(second_level_data) == 0:
            max_excellent_rate, max_pass_rate, max_avg_score, max_non_low_score_rate = 0, 0, 0, 0
        else:
            excellent_rates = [(sum(c['总分'] >= excellent_threshold)/len(c)) if len(c) > 0 else 0 for _, c in second_level_data.groupby('评估班级')]
            max_excellent_rate = self._safe_max(excellent_rates)
            pass_rates = [(sum(c['总分'] >= pass_threshold)/len(c)) if len(c) > 0 else 0 for _, c in second_level_data.groupby('评估班级')]
            max_pass_rate = self._safe_max(pass_rates)
            avg_scores = [c['总分'].mean() if len(c) > 0 else 0 for _, c in second_level_data.groupby('评估班级')]
            max_avg_score = self._safe_max(avg_scores)
            non_low_rates = [(1 - sum(c['总分'] < low_threshold)/len(c)) if len(c) > 0 else 0 for _, c in second_level_data.groupby('评估班级')]
            max_non_low_score_rate = self._safe_max(non_low_rates)
        
        # 计算全面评估各项积分 (使用配置权重)
        weight_excellent = rules['comp_weight_excellent']
        weight_pass = rules['comp_weight_pass']
        weight_avg = rules['comp_weight_avg']
        weight_non_low = rules['comp_weight_non_low']
        excellent_rate_score = (excellent_rate/max_excellent_rate) * weight_excellent if max_excellent_rate > 0 else 0
        pass_rate_score = (pass_rate/max_pass_rate) * weight_pass if max_pass_rate > 0 else 0
        avg_score_score = (avg_score/max_avg_score) * weight_avg if max_avg_score > 0 else 0
        non_low_rate_score = (non_low_score_rate/max_non_low_score_rate) * weight_non_low if max_non_low_score_rate > 0 else 0
        
        debug_info.append("\n--- 全面评估积分计算 ---")
        debug_info.append(f"最高优生率:{max_excellent_rate*100:03.2f}% (权重: {weight_excellent})")
        debug_info.append(f"最高合格率:{max_pass_rate*100:03.2f}% (权重: {weight_pass})")
        debug_info.append(f"最高平均分:{max_avg_score:.2f} (权重: {weight_avg})")
        debug_info.append(f"最高非低分率:{max_non_low_score_rate*100:03.2f}% (权重: {weight_non_low})")
        debug_info.append(f"优生率积分:{excellent_rate_score if excellent_rate_score.is_integer() else excellent_rate_score:.2f}")
        debug_info.append(f"合格率积分:{pass_rate_score if pass_rate_score.is_integer() else pass_rate_score:.2f}")
        debug_info.append(f"平均分积分:{avg_score_score if avg_score_score.is_integer() else avg_score_score:.2f}")
        debug_info.append(f"非低分率积分:{non_low_rate_score if non_low_rate_score.is_integer() else non_low_rate_score:.2f}")
        
        # !! 修正: 小计2直接累加,不乘以权重
        subtotal_2 = excellent_rate_score + pass_rate_score + avg_score_score + non_low_rate_score
        
        self._save_detailed_data(class_data, class_num, '__HeadTeacher__', '二层次', debug_info)

        # !! 修正: 总积分直接累加 小计1 和 小计2
        total_score_final = subtotal_1 + subtotal_2
        
        return {
            '班号': class_num,
            '班级人数': total_students,
            '教师姓名': self.class_teacher_map.get(class_num, ''),
            '层次': '二层次',
            '项目1': excellent_count,
            '积分1': score_1,
            '项目2': pass_count,
            '积分2': score_2,
            '小计1': subtotal_1, # 未加权小计1
            '优生数': sum(excellent_mask),
            '优生率': excellent_rate,
            '优生积分': excellent_rate_score, # 未加权
            '合格数': sum(pass_mask_base), 
            '合格率': pass_rate,
            '合格率积分': pass_rate_score, # 未加权
            '平均分': avg_score,
            '平均分积分': avg_score_score, # 未加权
            '低分数': low_score_count,
            '非低分率': non_low_score_rate,
            '非低分率积分': non_low_rate_score, # 未加权
            '小计2': subtotal_2, # 未加权小计2
            '总积分': total_score_final, # 修正后的总积分 (小计1+小计2)
            '科目名': self.class_team_label 
        }

    def _evaluate_first_level_teacher(self, class_data, subject, teacher_row):
        """一层次教师评估"""
        total_students = len(class_data)
        debug_info = []  # 用于存储调试信息
        
        debug_info.append(f"\n总人数:{total_students}") # 移到项目1之前
        
        # 检查科目是否存在于阈值配置中,如果不存在则跳过评估
        if subject not in self.subject_thresholds:
            print(f"警告:科目 '{subject}' 没有定义阈值,跳过教师 {teacher_row['教师姓名']} ({teacher_row['班号']}班) 的评估。")
            # 可以在这里返回一个空的或表示跳过的字典,或者根据需要处理
            # 为保持一致性,我们可能需要返回一个包含基本信息的字典,但评估值为0或NaN
            return {
                 '班号': teacher_row['班号'], '班级人数': total_students, '教师姓名': teacher_row['教师姓名'], '层次': '一层次',
                 '项目1': 0, '积分1': 0, '项目2': 0, '积分2': 0, '项目3': 0, '积分3': 0, '项目4': 0, '积分4': 0,
                 '小计1': 0, '优生数': 0, '优生率': 0, '优生积分': 0, '平均分': np.nan, '平均分积分': 0,
                 '低分数': 0, '非低分率': 0, '非低分率积分': 0, '小计2': 0, '总积分': 0, '科目名': subject
             }

        # 创建一个标记数组,用于记录已计分的学生
        counted_students = pd.Series(False, index=class_data.index)
        
        # --- 重点评估 --- 
        # 项目1:单科1-80名且总分进入1-80名(2分)
        subject_top_80_mask = class_data[f'{subject}_全校名次'] <= 80
        total_top_80_mask = class_data['总分_全校名次'] <= 80
        project1_mask_scored = subject_top_80_mask & total_top_80_mask
        project1_students_scored = class_data[project1_mask_scored]
        top_80_count = len(project1_students_scored) # 统计得分人数
        score_1 = top_80_count * 2
        counted_students = counted_students | project1_mask_scored # 更新计分标记
        
        # --- 移除 DEBUG ---
        # print(f"  DEBUG (一层次教师): T={teacher_row['教师姓名']}, C={teacher_row['班号']}, S={subject} - P1(单科<=80&总分<=80): Count={top_80_count}, Score={score_1}")
        # ------------------
        
        debug_info.append("\n--- 项目1:单科1-80名且总分进入1-80名(2分)---")
        for _, student in project1_students_scored.iterrows(): # 只记录得分学生
            debug_info.append(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, {subject}{student[subject]}, {subject}名次:{student[f'{subject}_全校名次']}, 总分:{student['总分']}, 总分名次:{student['总分_全校名次']}")
        debug_info.append(f"项目1人数:{top_80_count}, 得分:{score_1}")
        
        # 项目2:单科81-160名且总分进入81-160名(1.5分)
        subject_81_160_mask = (class_data[f'{subject}_全校名次'] > 80) & (class_data[f'{subject}_全校名次'] <= 160)
        total_81_160_mask = (class_data['总分_全校名次'] > 80) & (class_data['总分_全校名次'] <= 160)
        project2_mask_scored = subject_81_160_mask & total_81_160_mask & ~counted_students
        project2_students_scored = class_data[project2_mask_scored]
        top_160_count = len(project2_students_scored)
        score_2 = top_160_count * 1.5
        counted_students = counted_students | project2_mask_scored
        
        # --- 移除 DEBUG ---
        # print(f"  DEBUG (一层次教师): T={teacher_row['教师姓名']}, C={teacher_row['班号']}, S={subject} - P2(单科81-160&总分81-160): Count={top_160_count}, Score={score_2}")
        # ------------------
        
        debug_info.append("\n--- 项目2:单科81-160名且总分进入81-160名(1.5分)---")
        debug_info.append(f"{subject}名次范围:81-160名")
        debug_info.append(f"总分名次范围:81-160名")
        for _, student in project2_students_scored.iterrows(): # 只记录得分学生
            debug_info.append(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, {subject}{student[subject]}, {subject}名次:{student[f'{subject}_全校名次']}, 总分:{student['总分']}, 总分名次:{student['总分_全校名次']}")
        debug_info.append(f"项目2人数:{top_160_count}, 得分:{score_2}")
        
        # 项目3:单科优秀且总分优秀(1分)
        subject_excellent_mask_base = class_data[subject] >= self.subject_thresholds[subject]['excellent']
        # 使用动态科目列表计算总分优秀线
        total_excellent_threshold = sum([self.subject_thresholds[s]['excellent'] for s in self.SUBJECTS if s in self.subject_thresholds])
        total_excellent_mask = class_data['总分'] >= total_excellent_threshold
        project3_mask_scored = subject_excellent_mask_base & total_excellent_mask & ~counted_students
        project3_students_scored = class_data[project3_mask_scored]
        excellent_count = len(project3_students_scored)
        score_3 = excellent_count * 1
        counted_students = counted_students | project3_mask_scored
        
        # --- 移除 DEBUG ---
        # print(f"  DEBUG (一层次教师): T={teacher_row['教师姓名']}, C={teacher_row['班号']}, S={subject} - P3(单科优&总分优): Count={excellent_count}, Score={score_3}")
        # ------------------
        
        debug_info.append("\n--- 项目3:单科优秀且总分优秀(1分)---")
        debug_info.append(f"{subject}优秀线:{self.subject_thresholds[subject]['excellent']}")
        debug_info.append(f"总分优秀线:{total_excellent_threshold}")
        for _, student in project3_students_scored.iterrows(): # 只记录得分学生
            debug_info.append(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, {subject}{student[subject]}, 总分:{student['总分']}, {subject}名次:{student[f'{subject}_全校名次']}, 总分名次:{student['总分_全校名次']}")
        debug_info.append(f"项目3人数:{excellent_count}, 得分:{score_3}")
        
        # 项目4:单科优秀且总分合格(0.5分)
        # 使用动态科目列表计算总分合格线
        total_pass_threshold = sum([self.subject_thresholds[s]['pass'] for s in self.SUBJECTS if s in self.subject_thresholds])
        total_pass_mask = class_data['总分'] >= total_pass_threshold
        project4_mask_scored = subject_excellent_mask_base & total_pass_mask & ~counted_students
        project4_students_scored = class_data[project4_mask_scored]
        excellent_pass_count = len(project4_students_scored)
        score_4 = excellent_pass_count * 0.5
        counted_students = counted_students | project4_mask_scored

        # --- 移除 DEBUG ---
        # print(f"  DEBUG (一层次教师): T={teacher_row['教师姓名']}, C={teacher_row['班号']}, S={subject} - P4(单科优&总分合格): Count={excellent_pass_count}, Score={score_4}")
        # ------------------
        
        debug_info.append("\n--- 项目4:单科优秀且总分合格(0.5分)---")
        debug_info.append(f"{subject}优秀线:{self.subject_thresholds[subject]['excellent']}")
        debug_info.append(f"总分合格线:{total_pass_threshold}")
        for _, student in project4_students_scored.iterrows(): # 只记录得分学生
            debug_info.append(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, {subject}{student[subject]}, 总分:{student['总分']}, {subject}名次:{student[f'{subject}_全校名次']}, 总分名次:{student['总分_全校名次']}")
        debug_info.append(f"项目4人数:{excellent_pass_count}, 得分:{score_4}")
        
        # 修正:小计1 = 以上四项得分之和 (不加权)
        subtotal_1 = score_1 + score_2 + score_3 + score_4
        
        # --- 全面评估 --- 
        subject_excellent_rate = sum(subject_excellent_mask_base) / total_students # 使用基础优秀标记
        subject_avg_score = class_data[subject].mean()
        
        subject_low_mask = class_data[subject] < self.subject_thresholds[subject]['low']
        subject_non_low_rate = 1 - (sum(subject_low_mask) / total_students)
        subject_low_count = sum(subject_low_mask)
        
        debug_info.append("\n--- 全面评估数据 ---")
        debug_info.append(f"{subject}优秀线:{self.subject_thresholds[subject]['excellent']}")
        debug_info.append(f"{subject}合格线:{self.subject_thresholds[subject]['pass']}")
        debug_info.append(f"{subject}低分线:{self.subject_thresholds[subject]['low']}")
        debug_info.append(f"优生数:{sum(subject_excellent_mask_base)}") # 使用基础优秀标记计数
        debug_info.append(f"优生率:{subject_excellent_rate*100:03.2f}%")
        debug_info.append(f"平均分:{subject_avg_score:.2f}")
        debug_info.append(f"低分数:{subject_low_count}")
        debug_info.append(f"非低分人数:{total_students - sum(subject_low_mask)}")
        debug_info.append(f"非低分率:{subject_non_low_rate*100:03.2f}%")
        
        # 计算相对值(与同层次班级比较)
        first_level_data = self.scores_df[self.scores_df['层次_数值'] == 1]
        if len(first_level_data) == 0:
            max_excellent_rate = 0
            max_avg_score = 0
            max_non_low_rate = 0
        else:
            excellent_rates = [(sum(c[subject] >= self.subject_thresholds[subject]['excellent'])/len(c)) if len(c) > 0 else 0
                              for _, c in first_level_data.groupby('评估班级')]
            max_excellent_rate = self._safe_max(excellent_rates)
            
            avg_scores = [c[subject].mean() if len(c) > 0 else 0 for _, c in first_level_data.groupby('评估班级')]
            max_avg_score = self._safe_max(avg_scores)
            
            non_low_rates = [(1 - sum(c[subject] < self.subject_thresholds[subject]['low'])/len(c)) if len(c) > 0 else 0
                            for _, c in first_level_data.groupby('评估班级')]
            max_non_low_rate = self._safe_max(non_low_rates)
        
        # 计算全面评估各项积分
        excellent_rate_score = (subject_excellent_rate/max_excellent_rate) * 40 if max_excellent_rate > 0 else 0
        avg_score_score = (subject_avg_score/max_avg_score) * 40 if max_avg_score > 0 else 0
        non_low_rate_score = (subject_non_low_rate/max_non_low_rate) * 20 if max_non_low_rate > 0 else 0
        
        debug_info.append("\n--- 全面评估积分计算 ---")
        debug_info.append(f"最高优生率:{max_excellent_rate*100:03.2f}%")
        debug_info.append(f"最高平均分:{max_avg_score:.2f}")
        debug_info.append(f"最高非低分率:{max_non_low_rate*100:03.2f}%")
        debug_info.append(f"优生率积分:{excellent_rate_score if excellent_rate_score.is_integer() else excellent_rate_score:.2f}")
        debug_info.append(f"平均分积分:{avg_score_score if avg_score_score.is_integer() else avg_score_score:.2f}")
        debug_info.append(f"非低分率积分:{non_low_rate_score if non_low_rate_score.is_integer() else non_low_rate_score:.2f}")
        
        # 修正:小计2 = 以上三项得分之和 (不加权)
        subtotal_2 = excellent_rate_score + avg_score_score + non_low_rate_score
        
        # 保存详细数据
        self._save_detailed_data(class_data, teacher_row['班号'], subject, '一层次', debug_info)
        
        # 修正:总分 = (小计1 * 0.3) + (小计2 * 0.7)
        total_score_final = (subtotal_1 * 0.3) + (subtotal_2 * 0.7)

        return {
            '班号': teacher_row['班号'],
            '班级人数': total_students,
            '教师姓名': teacher_row['教师姓名'],
            '层次': '一层次',
            '项目1': top_80_count,
            '积分1': score_1,
            '项目2': top_160_count,
            '积分2': score_2,
            '项目3': excellent_count,
            '积分3': score_3,
            '项目4': excellent_pass_count,
            '积分4': score_4,
            '小计1': subtotal_1, # 返回未加权的小计1
            '优生数': sum(subject_excellent_mask_base), 
            '优生率': subject_excellent_rate,
            '优生积分': excellent_rate_score,
            '平均分': subject_avg_score,
            '平均分积分': avg_score_score,
            '低分数': subject_low_count,
            '非低分率': subject_non_low_rate,
            '非低分率积分': non_low_rate_score,
            '小计2': subtotal_2, # 返回未加权的小计2
            '总积分': total_score_final, # 返回最终加权总分
            '科目名': subject
        }

    def _evaluate_second_level_teacher(self, class_data, subject, teacher_row):
        """二层次教师评估"""
        total_students = len(class_data)
        debug_info = []  # 用于存储调试信息
        
        debug_info.append(f"\n总人数:{total_students}") # 移到项目1之前

        # 检查科目是否存在于阈值配置中
        if subject not in self.subject_thresholds:
            print(f"警告:科目 '{subject}' 没有定义阈值,跳过教师 {teacher_row['教师姓名']} ({teacher_row['班号']}班) 的评估。")
            return {
                 '班号': teacher_row['班号'], '班级人数': total_students, '教师姓名': teacher_row['教师姓名'], '层次': '二层次',
                 '项目1': 0, '积分1': 0, '项目2': 0, '积分2': 0, '小计1': 0,
                 '优生数': 0, '优生率': 0, '优生积分': 0, '合格数': 0, '合格率': 0, '合格率积分': 0,
                 '平均分': np.nan, '平均分积分': 0, '低分数': 0, '非低分率': 0, '非低分率积分': 0,
                 '小计2': 0, '总积分': 0, '科目名': subject
             }
        
        # --- 重点评估 --- 
        # 项目1:单科优秀且总分合格
        subject_excellent_mask_base = class_data[subject] >= self.subject_thresholds[subject]['excellent']
        # 使用动态科目列表计算总分合格线
        total_pass_threshold = sum([self.subject_thresholds[s]['pass'] for s in self.SUBJECTS if s in self.subject_thresholds])
        total_pass_mask = class_data['总分'] >= total_pass_threshold
        project1_mask_scored = subject_excellent_mask_base & total_pass_mask
        project1_students_scored = class_data[project1_mask_scored]
        excellent_pass_count = len(project1_students_scored) # 统计得分人数
        score_1 = excellent_pass_count * 2
        
        # --- 移除 DEBUG ---
        # if teacher_row['班号'] == 10 and subject == '历史':
        #     print(f"  DEBUG (二层次教师): T={teacher_row['教师姓名']}, C={teacher_row['班号']}, S={subject} - P1(单科优&总分合格): Count={excellent_pass_count}, Score={score_1}")
        # # ---------------------------

        debug_info.append("\n--- 项目1:单科优秀且总分合格(2分)---")
        debug_info.append(f"{subject}优秀线:{self.subject_thresholds[subject]['excellent']}")
        debug_info.append(f"总分合格线:{total_pass_threshold}")
        for _, student in project1_students_scored.iterrows(): # 只记录得分学生
            debug_info.append(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, {subject}{student[subject]}, 总分:{student['总分']}")
        debug_info.append(f"项目1人数:{excellent_pass_count}, 得分:{score_1}")
        
        # 项目2:单科合格且总分合格
        subject_pass_mask_base = class_data[subject] >= self.subject_thresholds[subject]['pass']
        # 计算实际得分的学生 (合格且非优秀)
        project2_mask_scored = subject_pass_mask_base & total_pass_mask & ~subject_excellent_mask_base 
        project2_students_scored = class_data[project2_mask_scored]
        pass_count = len(project2_students_scored) # 统计得分人数
        score_2 = pass_count * 1.5

        # --- 移除 DEBUG ---
        # if teacher_row['班号'] == 10 and subject == '历史':
        #     print(f"  DEBUG (二层次教师): T={teacher_row['教师姓名']}, C={teacher_row['班号']}, S={subject} - P2(单科合格&总分合格): Count={pass_count}, Score={score_2}")
        # # ---------------------------

        debug_info.append("\n--- 项目2:单科合格且总分合格(1.5分)---")
        debug_info.append(f"{subject}合格线:{self.subject_thresholds[subject]['pass']}")
        debug_info.append(f"总分合格线:{total_pass_threshold}")
        for _, student in project2_students_scored.iterrows(): # 只记录得分学生
            debug_info.append(f"准考证:{student['准考证']}, 姓名:{student['姓名']}, {subject}{student[subject]}, 总分:{student['总分']}")
        debug_info.append(f"项目2人数:{pass_count}, 得分:{score_2}")
        
        # 修正:计算未加权的小计1
        unweighted_subtotal_1 = score_1 + score_2
        
        # --- 全面评估 --- 
        subject_excellent_rate = sum(subject_excellent_mask_base) / total_students # 使用基础优秀标记
        subject_pass_rate = sum(subject_pass_mask_base) / total_students # 使用基础合格标记
        subject_avg_score = class_data[subject].mean()
        
        subject_low_mask = class_data[subject] < self.subject_thresholds[subject]['low']
        subject_non_low_rate = 1 - (sum(subject_low_mask) / total_students)
        subject_low_count = sum(subject_low_mask)
        
        debug_info.append("\n--- 全面评估数据 ---")
        debug_info.append(f"{subject}优秀线:{self.subject_thresholds[subject]['excellent']}")
        debug_info.append(f"{subject}合格线:{self.subject_thresholds[subject]['pass']}")
        debug_info.append(f"{subject}低分线:{self.subject_thresholds[subject]['low']}")
        debug_info.append(f"优生数:{sum(subject_excellent_mask_base)}") # 使用基础优秀标记计数
        debug_info.append(f"优生率:{subject_excellent_rate*100:03.2f}%")
        debug_info.append(f"合格数:{sum(subject_pass_mask_base)}") # 使用基础合格标记计数
        debug_info.append(f"合格率:{subject_pass_rate*100:03.2f}%")
        debug_info.append(f"平均分:{subject_avg_score:.2f}")
        debug_info.append(f"低分数:{subject_low_count}")
        debug_info.append(f"非低分人数:{total_students - sum(subject_low_mask)}")
        debug_info.append(f"非低分率:{subject_non_low_rate*100:03.2f}%")
        
        # 计算相对值(与同层次班级比较)
        second_level_data = self.scores_df[self.scores_df['层次_数值'] == 2]
        if len(second_level_data) == 0:
            max_excellent_rate = 0
            max_pass_rate = 0
            max_avg_score = 0
            max_non_low_rate = 0
        else:
            excellent_rates = [(sum(c[subject] >= self.subject_thresholds[subject]['excellent'])/len(c)) if len(c) > 0 else 0
                              for _, c in second_level_data.groupby('评估班级')]
            max_excellent_rate = self._safe_max(excellent_rates)
            
            pass_rates = [(sum(c[subject] >= self.subject_thresholds[subject]['pass'])/len(c)) if len(c) > 0 else 0
                         for _, c in second_level_data.groupby('评估班级')]
            max_pass_rate = self._safe_max(pass_rates)
            
            avg_scores = [c[subject].mean() if len(c) > 0 else 0 for _, c in second_level_data.groupby('评估班级')]
            max_avg_score = self._safe_max(avg_scores)
            
            non_low_rates = [(1 - sum(c[subject] < self.subject_thresholds[subject]['low'])/len(c)) if len(c) > 0 else 0
                            for _, c in second_level_data.groupby('评估班级')]
            max_non_low_rate = self._safe_max(non_low_rates)
        
        # 计算全面评估各项积分
        excellent_rate_score = (subject_excellent_rate/max_excellent_rate) * 5 if max_excellent_rate > 0 else 0
        pass_rate_score = (subject_pass_rate/max_pass_rate) * 30 if max_pass_rate > 0 else 0
        avg_score_score = (subject_avg_score/max_avg_score) * 30 if max_avg_score > 0 else 0
        non_low_rate_score = (subject_non_low_rate/max_non_low_rate) * 35 if max_non_low_rate > 0 else 0
        
        debug_info.append("\n--- 全面评估积分计算 ---")
        debug_info.append(f"最高优生率:{max_excellent_rate*100:03.2f}%")
        debug_info.append(f"最高合格率:{max_pass_rate*100:03.2f}%")
        debug_info.append(f"最高平均分:{max_avg_score:.2f}")
        debug_info.append(f"最高非低分率:{max_non_low_rate*100:03.2f}%")
        debug_info.append(f"优生率积分:{excellent_rate_score if excellent_rate_score.is_integer() else excellent_rate_score:.2f}")
        debug_info.append(f"合格率积分:{pass_rate_score if pass_rate_score.is_integer() else pass_rate_score:.2f}")
        debug_info.append(f"平均分积分:{avg_score_score if avg_score_score.is_integer() else avg_score_score:.2f}")
        debug_info.append(f"非低分率积分:{non_low_rate_score if non_low_rate_score.is_integer() else non_low_rate_score:.2f}")
        
        # 修正:计算未加权的小计2
        unweighted_subtotal_2 = excellent_rate_score + pass_rate_score + avg_score_score + non_low_rate_score
        
        # 保存详细数据
        self._save_detailed_data(class_data, teacher_row['班号'], subject, '二层次', debug_info)
        
        # 修正:总分 = (未加权小计1 * 0.2) + (未加权小计2 * 0.8)
        total_score_final = (unweighted_subtotal_1 * 0.2) + (unweighted_subtotal_2 * 0.8)

        # --- 移除临时调试打印 --- 
        # if teacher_row['班号'] == 10 and subject == '历史':
        #       # 打印未加权的小计和最终总分
        #       print(f"DEBUG (二层次教师): 10班 历史 - 返回前的值: 未加权小计1={unweighted_subtotal_1}, 未加权小计2={unweighted_subtotal_2}, 总积分={total_score_final}") 
        # # --- 结束临时调试 --- 

        return {
            '班号': teacher_row['班号'],
            '班级人数': total_students,
            '教师姓名': teacher_row['教师姓名'],
            '层次': '二层次',
            '项目1': excellent_pass_count,
            '积分1': score_1,
            '项目2': pass_count,
            '积分2': score_2,
            '小计1': unweighted_subtotal_1, # 返回未加权的小计1
            '优生数': sum(subject_excellent_mask_base), # 使用基础优秀标记计数
            '优生率': subject_excellent_rate,
            '优生积分': excellent_rate_score,
            '合格数': sum(subject_pass_mask_base), # 使用基础合格标记计数
            '合格率': subject_pass_rate,
            '合格率积分': pass_rate_score,
            '平均分': subject_avg_score,
            '平均分积分': avg_score_score,
            '低分数': subject_low_count,
            '非低分率': subject_non_low_rate,
            '非低分率积分': non_low_rate_score,
            '小计2': unweighted_subtotal_2, # 返回未加权的小计2
            '总积分': total_score_final, # 最终加权总分
            '科目名': subject
        }

    def evaluate_class_team(self):
        """班团评估"""
        results = []
        
        for class_num in sorted(self.scores_df['评估班级'].unique()):
            class_data = self.scores_df[self.scores_df['评估班级'] == class_num].copy()
            if class_data.empty:
                continue
            level = class_data['层次_数值'].iloc[0]
            
            if level == 1:
                # 调用班级评估函数,它内部会使用配置的标签返回结果
                result = self._evaluate_first_level_class(class_data, class_num)
            else:
                result = self._evaluate_second_level_class(class_data, class_num)
                
            results.append(result)
            
        return pd.DataFrame(results)

    def evaluate_teachers(self):
        """教师评估"""
        results = []
        
        # 过滤掉班主任记录 (使用内部标记),只评估学科教师
        subject_teachers = self.teachers_df[self.teachers_df['学科'] != '__HeadTeacher__']
        
        for _, teacher_row in subject_teachers.iterrows():
            subject = teacher_row['学科']
            class_num = teacher_row['班号']
            
            teacher_class_data = self.scores_df[self.scores_df['评估班级'] == class_num].copy()
            if teacher_class_data.empty:
                print(f"警告:班级 {class_num} 在成绩表中没有数据,跳过教师 {teacher_row['教师姓名']} ({subject}) 的评估。")
                continue
                
            level = teacher_class_data['层次_数值'].iloc[0]
            
            if subject not in teacher_class_data.columns:
                 print(f"警告:科目 '{subject}' 不在班级 {class_num} 的成绩数据中,跳过教师 {teacher_row['教师姓名']} 的评估。")
                 continue

            # 检查科目阈值是否存在 (教师评估函数内部已有此检查)
            # if subject not in self.subject_thresholds: ...

            if level == 1:
                result = self._evaluate_first_level_teacher(teacher_class_data, subject, teacher_row)
            else:
                result = self._evaluate_second_level_teacher(teacher_class_data, subject, teacher_row)
                
            if result: 
                results.append(result)
            
        return pd.DataFrame(results)

    def save_results(self, exam_name):
        """保存评估结果"""
        output_path = None
        try:
            # 清空详细数据目录下的 .txt 文件
            if os.path.exists(self.detailed_data_dir):
                print(f"清空目录:{self.detailed_data_dir}")
                for filename in os.listdir(self.detailed_data_dir):
                    if filename.endswith(".txt"):
                        file_path = os.path.join(self.detailed_data_dir, filename)
                        try:
                            os.remove(file_path)
                            print(f"  已删除: {filename}")
                        except Exception as e:
                            print(f"无法删除文件 {file_path}: {e}")
            else:
                 # 如果目录不存在,则创建它
                 print(f"创建目录:{self.detailed_data_dir}")
                 os.makedirs(self.detailed_data_dir)

            # --- 计算评估 --- 
            print("正在进行班团评估...")
            class_results = self.evaluate_class_team()
            print("正在进行教师评估...")
            teacher_results = self.evaluate_teachers()
            # ------------------
            
            # 检查是否有评估结果
            if class_results.empty and teacher_results.empty:
                print("警告:没有生成任何评估结果。请检查输入文件和配置。")
                return None # 如果都没有结果,则不继续执行保存
            elif class_results.empty:
                 print("警告:班团评估未生成结果。")
            elif teacher_results.empty:
                 print("警告:教师评估未生成结果。")

            # --- 合并和处理结果 --- 
            # 移除错误的总积分重新计算
            # if not class_results.empty:
            #     class_results['总积分'] = class_results.apply(lambda row: row.get('小计1', 0) + row.get('小计2', 0), axis=1)
            # if not teacher_results.empty:
            #     teacher_results['总积分'] = teacher_results.apply(lambda row: row.get('小计1', 0) + row.get('小计2', 0), axis=1)
            
            # 合并结果 (处理可能为空的DataFrame)
            results_list = []
            if not class_results.empty:
                results_list.append(class_results)
            if not teacher_results.empty:
                results_list.append(teacher_results)
            
            if not results_list:
                 print("错误:无法合并结果,因为两个评估结果都为空。")
                 return None
                 
            results = pd.concat(results_list, ignore_index=True)

            # 添加名次列 (基于 '总积分' 列)
            if '总积分' in results.columns:
                results['名次'] = results.groupby(['科目名', '层次'])['总积分'].rank(method='min', ascending=False).astype(int)
            else:
                 results['名次'] = np.nan # 如果没有总积分,名次无意义
            
            # 创建科目名的排序顺序 (使用配置的班团标签和科目列表)
            # 注意:这里使用 self.class_team_label
            subject_order = [self.class_team_label] + sorted(list(self.SUBJECTS))
            present_subjects = results['科目名'].unique()
            final_subject_order = [s for s in subject_order if s in present_subjects]
            for s in present_subjects:
                if s not in final_subject_order:
                    final_subject_order.append(s)
            results['科目名排序'] = pd.Categorical(results['科目名'], categories=final_subject_order, ordered=True)
            
            # --- 格式化 --- 
            def format_number(x):
                if pd.isna(x):
                    return x
                if isinstance(x, (int, float)):
                    # 检查是否非常接近整数
                    if np.isclose(x, round(x)):
                        return int(round(x))
                    # 修改:保留一位小数
                    return round(x, 1) 
                return x

            def format_percentage(x):
                if pd.isna(x):
                    return x
                if isinstance(x, (int, float)):
                    # 确保百分比格式为048.39%
                    return f"{x*100:03.2f}%"
                return x # 如果不是数字,直接返回

            # 应用格式化
            format_cols = results.select_dtypes(include=[np.number]).columns
            percent_cols = ['优生率', '合格率', '非低分率']
            
            for col in format_cols:
                if col in percent_cols:
                    results[col] = results[col].apply(format_percentage)
                elif col not in ['班号', '班级人数', '名次']: # 这些通常是整数
                    results[col] = results[col].apply(format_number)
            # ----------------

            # --- 排序 --- 
            # 按要求排序:科目名、层次、班号 (逻辑保持不变)
            sort_columns = ['科目名排序', '层次']
            if '班号' in results.columns: 
                 sort_columns.append('班号')
            results = results.sort_values(by=sort_columns, ascending=[True] * len(sort_columns))
            results = results.drop('科目名排序', axis=1)
            # -------------

            # --- 重新排列列顺序 (使用 '总积分' 替换 '总分') --- 
            expected_columns_order = [
                '班号', '班级人数', '名次', '教师姓名',
                '总积分', # 使用正确的 '总积分' 列
                '项目1', '积分1', '项目2', '积分2', '项目3', '积分3', '项目4', '积分4', '小计1', # 未加权
                '优生数', '优生率', '优生积分',
                '合格数', '合格率', '合格率积分',
                '平均分', '平均分积分',
                '低分数', '非低分率', '非低分率积分', '小计2', # 未加权
                '科目名', '层次'
            ]
            
            # 获取实际存在的列,并按期望顺序排列
            available_columns = [col for col in expected_columns_order if col in results.columns]
            results = results[available_columns]
            # ---------------------

            # --- 新增:文件复制、命名、写入标题和数据 ---
            # 检查合并后的 results 是否为空
            if results.empty:
                print("警告:评估结果为空,无法生成输出文件。")
                return None

            # 1. 生成时间戳和输出文件名
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_filename = f"{exam_name}_{timestamp}.xlsx"
            # 将输出文件路径设置在详细数据目录内
            output_path = os.path.join(self.detailed_data_dir, output_filename)

            # 2. 复制模板文件 (保持不变)
            print(f"复制 {self.grade} 年级模板文件 '{self.template_file}' 到 '{output_path}'")
            shutil.copy(self.template_file, output_path)

            # 3. 使用 openpyxl 将结果 DataFrame 数据写入复制的模板(从第4行开始,保留模板原有内容)
            print(f"将评估结果数据写入文件 '{output_path}' (从第4行开始,保留模板表头)...")
            workbook = load_workbook(output_path)
            sheet = workbook.active

            # 新增:在第一行A1单元格写入考试名称
            print(f"在 A1 单元格写入考试名称: {exam_name}")
            sheet['A1'] = exam_name + " 评估结果"

            # 定义数据写入的起始行 (Excel行号,1-based)
            start_excel_row = 4 
            
            # 将格式化后的 DataFrame 数据转换为适合 openpyxl 写入的列表
            # 注意:此时 results DataFrame 中的数据已经是格式化过的字符串或数字
            data_to_write = results.values.tolist()

            # 逐行逐单元格写入数据
            for r_idx, row_data in enumerate(data_to_write):
                # 计算当前要写入的 Excel 行号
                current_excel_row = start_excel_row + r_idx
                for c_idx, value in enumerate(row_data):
                    # 计算当前要写入的 Excel 列号 (1-based)
                    current_excel_col = c_idx + 1
                    # 直接写入值 (因为之前已经格式化过)
                    sheet.cell(row=current_excel_row, column=current_excel_col, value=value)
            
            # 保存对工作簿的修改
            workbook.save(output_path)
            # --------------------------------------------------

            print(f"评估结果已成功写入复制的模板: {output_path}")
            return output_path # 返回成功生成的文件路径

        except FileNotFoundError as e: # 特别处理文件复制时的错误
             print(f"文件操作错误:{str(e)}")
             print(f"请确保 {self.grade} 年级的模板文件 '{self.template_file}' (来自配置) 存在。")
             return None # 返回 None 表示失败
        except KeyError as e:
             print(f"保存结果时出现配置或列名错误:'{e}'。可能是科目名排序或列重排时出错。")
             print(f"请检查配置文件 ({CONFIG_FILE}) 和评估结果 DataFrame 的列名。")
             return None
        except Exception as e:
            print(f"保存结果到复制模板时出错:{str(e)}")
            import traceback
            traceback.print_exc()
            # 可以在这里决定是否需要回退到旧的保存方式,或者直接返回失败
            # print(f"尝试保存到默认位置...") # 移除旧的回退逻辑
            return None # 返回 None 表示失败

def main():
    global CONFIG # 声明要修改全局 CONFIG 变量
    try:
        # --- 程序启动时首先加载配置 --- 
        load_config()
        # -----------------------------

        # 获取配置文件中支持的年级列表 (并确保是字符串)
        supported_grades = [str(k) for k in CONFIG['grades'].keys()]

        # 获取用户输入的年级
        while True:
            try:
                grade_input = input(f"请输入要评估的年级 ({', '.join(supported_grades)}):")
                # 不需要转换成 int,因为配置查找使用字符串 <- 旧注释,现在需要转换
                if grade_input in supported_grades:
                    grade = int(grade_input) # 转换回整数
                    break
                else:
                    print(f"错误:不支持的年级 '{grade_input}'。请输入 {', '.join(supported_grades)} 中的一个。")
            except ValueError: # 添加对 int 转换失败的处理
                print(f"错误:输入的 '{grade_input}' 不是有效的数字年级。")
            except Exception as e: 
                print(f"输入年级时发生错误: {e}")
                
        # 获取考试名称 (保持不变)
        exam_name = input("请输入本次考试的名称:")
        if not exam_name: 
            # 注意:grade 现在是整数,如果需要拼接,需要转回字符串
            exam_name = f"{str(grade)}年级评估" 
            print(f"未输入考试名称,将使用默认名称:'{exam_name}'")

        print(f"开始 {grade} 年级 '{exam_name}' 评估程序...")
        # 使用整数年级初始化
        evaluator = ScoreEvaluator(grade)
        
        print("正在准备数据...")
        evaluator.prepare_data()
        
        print("正在保存评估结果...")
        output_file_path = evaluator.save_results(exam_name)
        
        # 结果处理和输出 (保持不变)
        if output_file_path:
            print("-" * 30)
            print(f"{grade} 年级 '{exam_name}' 评估完成!")
            print(f"评估结果已保存到文件: {output_file_path}") 
            print(f"详细数据日志已保存到目录: {evaluator.detailed_data_dir}")
            print("-" * 30)
        else:
             print("-" * 30)
             print(f"{grade} 年级 '{exam_name}' 评估过程中发生错误,未能成功生成结果文件。")
             print("请检查上面的错误信息。")
             print("-" * 30)
        
    except (FileNotFoundError, ValueError, yaml.YAMLError, RuntimeError, ImportError) as e:
         # 捕获配置文件加载、数据准备、库缺失等特定错误
         print(f"程序运行失败:{str(e)}")
         # 可以在这里添加更详细的指引,例如检查配置文件路径、内容、Excel文件等
         print("程序终止")
    except Exception as e:
        print(f"发生未知错误:{str(e)}")
        import traceback
        traceback.print_exc()
        print("程序终止")

if __name__ == '__main__':
    main() 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值