#  Copyright (c) 2025-2026  Jeffrey Hsu - JITToolBox
#  #
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#  #
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  #
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <https://www.gnu.org/licenses/>.

import datetime
import traceback
import io
from typing import Optional, Callable

import openpyxl
from openpyxl.utils import get_column_letter, column_index_from_string
from openpyxl.workbook.workbook import Workbook
from openpyxl.worksheet.worksheet import Worksheet
from packaging import version
from PIL import Image

from module import LOGLEVEL, COMPATIBLE_VERSION
from module.schema import Performance
from utils.function import format_ranges, get_rank, min_score_people_name, get_class_index_range, check_version, \
    gen_picture


class ExcelReader:
    kpi_list: list[str]
    hml_list: list[str]
    kpi_number: int
    file_path: str
    course_name: str
    course_teacher_name: str
    total_class_str: str
    class_list: list[str]
    class_number: list[int]
    course_objectives: list[str]
    course_objectives_number: list[int]
    evaluation_stage: list[tuple[Optional[str], Optional[str]]]
    achievement_level: list[list[Performance]]
    target_score: list[list[float]]
    n_evaluation_methods: list[Optional[str]]
    question_data: dict[str, list[tuple[str, int]]]
    ignore_version_check: bool
    pic_list: list
    suggestion_template_list: list[Optional[str]]
    major_director_signature_image: Optional[Image.Image]
    course_leader_signature_image: Optional[Image.Image]

    class _SheetImageLoader:
        """Lightweight image loader scoped for ExcelReader use."""

        def __init__(self, sheet: Worksheet):
            self._images: dict[str, Callable[[], bytes]] = {}
            for image in getattr(sheet, "_images", []):
                row = image.anchor._from.row + 1
                col = get_column_letter(image.anchor._from.col + 1)
                self._images[f"{col}{row}"] = image._data

        def image_in(self, cell: str) -> bool:
            return cell in self._images

        def get(self, cell: str) -> Image.Image:
            if cell not in self._images:
                raise ValueError(f"Cell {cell} doesn't contain an image")
            image = io.BytesIO(self._images[cell]())
            return Image.open(image)

    class ValidError(Exception):
        def __init__(self, message):
            self.message = message

        def __str__(self):
            return self.message

    def __init__(self, file_path: str, version_check: bool = False,
                 signal: Callable[[str, str], None] = lambda x, y: print(x)):
        super().__init__()
        self.file_path = file_path
        self.kpi_list = []
        self.class_list = []
        self.class_number = []
        self.course_objectives = []
        self.course_objectives_number = []
        self.course_name = ""
        self.course_teacher_name = ""
        self.course_lead_teacher_name = ""
        self.evaluation_stage = []
        self.achievement_level = []
        self.kpi_number = 0
        self.stu_kpi_achieve_list = []
        self.target_score = []
        self.n_evaluation_methods = []
        self.total_class_str = ""
        self.hml_list = []
        self.question_data = {}
        self.ignore_version_check = version_check
        self.pic_list = []
        self.signal = signal
        self.suggestion_template_list = []
        self.major_director_signature_image = None
        self.course_leader_signature_image = None

    def parse_excel(self):
        try:
            wb: Workbook = openpyxl.load_workbook(self.file_path, read_only=True, data_only=True)
            sheet: Worksheet = wb['初始录入']
            # 读取版本号
            e_version = sheet['V4'].value if sheet['V4'].value is not None else sheet['U4'].value
            e_version = sheet['H1'].value if e_version is None else e_version
            if e_version is None:
                e_version = "0"
            status, _ = check_version(e_version, COMPATIBLE_VERSION)
            CUR_VERSION = version.parse(e_version)
            if not status:
                if not self.ignore_version_check:
                    raise NotImplementedError(f"版本不适配，当前表格版本：{e_version}，适配版本：{COMPATIBLE_VERSION}")
                self.signal("已忽略表格兼容性：您的表格版本不在适配版本中，可能导致未知错误。", LOGLEVEL.WARNING)
            # 读取教学班级
            self.total_class_str = sheet["D10"].value
            # 读取课程名称
            match CUR_VERSION:
                case _ if CUR_VERSION <= version.parse("7.3"):
                    self.course_name = sheet["D6"].value
                case _ if CUR_VERSION >= version.parse("7.4"):
                    self.course_name = sheet["D5"].value
            # 读取任课教师
            self.course_teacher_name = sheet["D7"].value
            # 读取课程负责人
            self.course_lead_teacher_name = sheet["D8"].value

            need_signature_images = CUR_VERSION >= version.parse("9.4") and sheet["H10"].value == "是"
            if need_signature_images:
                self._load_signature_images()
            else:
                self.major_director_signature_image = None
                self.course_leader_signature_image = None

            # 读取班级和人数
            max_class_size = 4
            match CUR_VERSION:
                case _ if CUR_VERSION <= version.parse("7.6"):
                    max_class_size = 3

            for i in range(2, 2 + max_class_size):
                name = sheet[f"K{i}"]
                number = sheet[f"M{i}"]
                if name.value is None or number.value is None:
                    break
                self.class_list.append(name.value)
                self.class_number.append(int(number.value))
            # 读取课程指标个数
            self.kpi_number = sheet["H8"].value
            # 读取课程目标和指标点
            hml_start = 22
            match CUR_VERSION:
                case _ if CUR_VERSION <= version.parse("7.6"):
                    hml_start = 21
            for i in range(hml_start, hml_start + 5):
                hml = sheet[f"H{i}"]
                objective = sheet[f"I{i}"]
                kpi = sheet[f"Q{i}"]
                if kpi.value is None or objective.value is None:
                    break
                self.hml_list.append(hml.value)
                self.kpi_list.append(kpi.value)
                self.course_objectives.append(objective.value)
            # 读取考核方式及权重
            k_start = 7
            match CUR_VERSION:
                case _ if CUR_VERSION <= version.parse("7.6"):
                    k_start = 6
            for i in range(k_start, k_start + 6):
                if i in (k_start + 1, k_start + 2, k_start + 4):
                    continue
                way = sheet[f"K{i}"]
                per = sheet[f"L{i}"]
                self.evaluation_stage.append((way.value, per.value))

            # 读取平时考核方式
            end = k_start + 5
            if CUR_VERSION >= version.parse("7.7"):
                end += 1
            for i in range(k_start, end):
                val = sheet[f"M{i}"].value
                self.n_evaluation_methods.append(val)

            sheet = wb['达成度']
            # 目标值表格列                                                  |              目标X              |
            kpi_row_tuple = (  # | 平时 | 大作业 | 试卷 | 个人达成值 |
                ('D', 'E', 'F', 'G'),  # 目标一                      平均分 |                                |
                ('H', 'I', 'J', 'K'),  # 目标二                      得分率 |                                |
                ('L', 'M', 'N', 'O'),  # 目标三                      达成度 |                                |
                ('P', 'Q', 'R', 'S'),  # 目标四
                ('T', 'U', 'V', 'W')  # 目标五
            )
            # 获取达成度中的目标分值、班级平均分，得分率和达成度
            for i in range(self.kpi_number):
                # 目标分值
                self.target_score.append([
                    sheet[kpi_row_tuple[i][0] + f"6"].value,
                    sheet[kpi_row_tuple[i][1] + f"6"].value,
                    sheet[kpi_row_tuple[i][2] + f"6"].value
                ])
                base_row = 8
                for j in range(len(self.class_number)):
                    # 平均分
                    avg = (
                        sheet[kpi_row_tuple[i][0] + f"{base_row + j * 3}"].value,
                        sheet[kpi_row_tuple[i][1] + f"{base_row + j * 3}"].value,
                        sheet[kpi_row_tuple[i][2] + f"{base_row + j * 3}"].value
                    )
                    # 得分率
                    per = (
                        sheet[kpi_row_tuple[i][0] + f"{base_row + 1 + j * 3}"].value,
                        sheet[kpi_row_tuple[i][1] + f"{base_row + 1 + j * 3}"].value,
                        sheet[kpi_row_tuple[i][2] + f"{base_row + 1 + j * 3}"].value
                    )
                    # 达成度
                    kpi_c = sheet[kpi_row_tuple[i][0] + f"{base_row + 2 + j * 3}"].value
                    if len(self.achievement_level) - 1 < j:
                        self.achievement_level.append([Performance(avg, per, kpi_c)])
                    else:
                        self.achievement_level[j].append(Performance(avg, per, kpi_c))
            # 获取学生的各项目标值
            # 读取 A18:O87 范围的数据
            match CUR_VERSION:
                case _ if CUR_VERSION <= version.parse("7.6"):
                    rows = sheet[f'B18:{kpi_row_tuple[self.kpi_number - 1][3]}{17 + sum(self.class_number)}']
                case _:
                    rows = sheet[f'B21:{kpi_row_tuple[self.kpi_number - 1][3]}{20 + sum(self.class_number)}']

            # 将数据按行保存到列表中
            for row in rows:
                row_data = [cell.value for cell in row]
                self.stu_kpi_achieve_list.append(row_data)

            # 获取试题信息
            sheet = wb['成绩录入']
            if CUR_VERSION < version.parse("9.7"):
                start_col = 'BC'  # 起始列
            else:
                start_col = 'BH'
            row_keys = 9  # 键所在的行
            row_values = [7, 8]  # 值所在的行

            # 确定终止列
            col = start_col
            while True:
                if not sheet[col + str(row_keys)].value:
                    break
                col = get_column_letter(column_index_from_string(col) + 1)

            # 读取数据
            for col_index in range(column_index_from_string(start_col),
                                   column_index_from_string(col)):
                col_letter = get_column_letter(col_index)
                key = sheet[col_letter + str(row_keys)].value
                if key:  # 确保键不为空
                    values = tuple(sheet[col_letter + str(r)].value for r in row_values)
                    if key in self.question_data:
                        self.question_data[key].append(values)
                    else:
                        self.question_data[key] = [values]

            # 读取建议模板
            if CUR_VERSION >= version.parse("9.0"):
                sheet = wb['初始录入']

                for i in range(29, 34):
                    self.suggestion_template_list.append(sheet[f'I{i}'].value)

            if len(self.suggestion_template_list) != 5:
                for i in range(len(self.suggestion_template_list), 5):
                    self.suggestion_template_list.append(None)

            if vd_lst := self.validate_data():
                raise self.ValidError("\n\n".join(vd_lst))

            self.gen_picture()

        except self.ValidError as ve:
            raise Exception(f"""
                        数据验证失败：\n\n{str(ve)}
                        """)
        except Exception as e:
            error_message = traceback.format_exc()
            raise Exception(f"""
            原始错误：
            {error_message}
            """)

    def set_file_path(self, file_path: str):
        self.file_path = file_path

    def validate_data(self) -> list[str]:
        lst: list[str] = []
        self.signal("正在验证数据", LOGLEVEL.INFO)
        if len(self.kpi_list) != self.kpi_number:
            self.signal("\"课程目标\"或\"目标支撑的毕业要求指标点\"数量与期望目标数量不符", LOGLEVEL.ERROR)
            lst.append(
                f"\"课程目标\"或\"目标支撑的毕业要求指标点\"数量与期望目标数量不符，请检查Excel表格中的\"课程目标\"和\"目标支撑的毕业要求指标点\"列是否填写完整。"
                f"期望得到 {self.kpi_number} 个，实际检测到 {len(self.kpi_list)} 个。"
                f"（如想暂时不填，请在Excel表格对应的位置添加一个空格）"
            )

        return lst

    def run(self):
        self.parse_excel()

    def _load_signature_images(self):
        signature_cells = {
            "major_director_signature_image": ("I34", "K34"),
            "course_leader_signature_image": ("I35", "K35"),
        }
        wb_with_images: Workbook = openpyxl.load_workbook(self.file_path, data_only=True)
        try:
            sheet_with_images: Worksheet = wb_with_images["初始录入"]
            loader = self._SheetImageLoader(sheet_with_images)

            for attr, (check_cell, image_cell) in signature_cells.items():
                if sheet_with_images[check_cell].value and loader.image_in(image_cell):
                    setattr(self, attr, loader.get(image_cell))
                else:
                    setattr(self, attr, None)
        finally:
            wb_with_images.close()

    def clear_all_data(self):
        self.kpi_list = []
        self.kpi_number = 0
        self.file_path = ""
        self.course_name = ""
        self.course_teacher_name = []
        self.class_list = []
        self.class_number = []
        self.course_objectives = []
        self.course_objectives_number = []
        self.evaluation_stage = []
        self.achievement_level = []
        self.kpi_number = 0
        self.stu_kpi_achieve_list = []
        self.target_score = []
        self.n_evaluation_methods = []
        self.hml_list = []
        self.question_data = {}
        self.pic_list = []
        self.major_director_signature_image = None
        self.course_leader_signature_image = None

    def set_version_check(self, version_check: bool):
        self.ignore_version_check = version_check

    def gen_picture(self):
        self.signal("正在生成散点图", LOGLEVEL.INFO)
        for i in range(len(self.class_list)):
            l_index, r_index = get_class_index_range(self.class_number, i)
            self.pic_list.append(gen_picture(
                self.stu_kpi_achieve_list,
                self.kpi_number,
                l_index,
                r_index
            ))

    def get_word_template(self, class_index):
        yield "课程目标"
        for i in range(self.kpi_number):
            yield f"课程目标{i + 1}（{self.hml_list[i]}）"
        yield ("总成绩=\n{}".
        format(
            "\n+".join([f"{n if n != '试卷' else '期末'}成绩×{int(p * 100)}%" for n, p in self.evaluation_stage if
                        p is not None])))
        for i in self.course_objectives:
            yield i
        yield "支撑毕业要求指标点"
        for i in self.kpi_list:
            yield i
        yield "考核类型"
        for i in range(self.kpi_number):
            s_lst = self.achievement_level[class_index][i].scores
            for index, (j, s) in enumerate(zip(self.evaluation_stage, s_lst)):
                if s is None:
                    continue
                if index == 2:
                    if len(self.n_evaluation_methods) == 6:
                        if self.n_evaluation_methods[5] == "试卷":
                            yield "期末考核\n（试卷）"
                        else:
                            yield "期末考核"
                    elif j[0] == "试卷":
                        yield "期末考核\n（试卷）"
                    else:
                        yield "期末考核"
                else:
                    yield f"{j[0]}考核"

        yield "目标分值（分）"
        for i in self.target_score:
            for j in i:
                if j is None:
                    continue
                yield j
        yield "权重"
        for i in range(self.kpi_number):
            s_lst = self.achievement_level[class_index][i].scores
            for j, s in zip(self.evaluation_stage, s_lst):
                if s is None:
                    continue
                yield j[1]
        yield "学号"
        yield "姓名"
        for i in range(self.kpi_number):
            s_lst = self.achievement_level[class_index][i].scores
            for (index, j), s in zip(enumerate(self.evaluation_stage), s_lst):
                if s is None:
                    continue
                match index:
                    case 0:
                        yield "\n".join([x for x in self.n_evaluation_methods[:3] if x is not None])
                    case 1:
                        yield "\n".join([x for x in self.n_evaluation_methods[3:5] if x is not None])
                    case 2:
                        if len(self.n_evaluation_methods) == 6 and self.n_evaluation_methods[5] != "试卷":
                            yield self.n_evaluation_methods[5]
                        elif len(self.n_evaluation_methods) == 5 and self.evaluation_stage[2][0] != "试卷":
                            yield self.evaluation_stage[2][0]
                        else:
                            # 中文数字到数字的映射
                            chinese_num_map = {'一': 1, '二': 2, '三': 3,
                                               '四': 4, '五': 5, '六': 6,
                                               '七': 7, '八': 8, '九': 9}
                            q_lst = self.question_data[f'目标{i + 1}']
                            q_dict = {key: [item[1] for item in q_lst if item[0] == key] for key in
                                      set(key for key, _ in q_lst)}
                            q_str_lst = [f"{k[0]}、{k[1:]}：{format_ranges(v)}" for k, v in q_dict.items()]
                            q_str_lst.sort(key=lambda x: chinese_num_map[x[0]])
                            q_str = "\n".join(q_str_lst)
                            yield q_str
                    case _:
                        yield "如果你能看到本行文字，请联系开发者"

        l_range, r_range = get_class_index_range(self.class_number, class_index)
        data = self.stu_kpi_achieve_list[l_range:r_range]
        # 要移除的索引位置
        remove_indices = [5, 9, 13, 17, 21]
        # 更新后的数据
        updated_data = [[item for j, item in enumerate(row) if j not in remove_indices] for row in data]
        for i in updated_data:
            for j in i:
                if j is None:
                    continue
                yield j
        yield "平均得分"
        for i in range(self.kpi_number):
            avg_scores_lst = self.achievement_level[class_index][i].scores
            for j in avg_scores_lst:
                if j is None:
                    continue
                yield j
        yield "得分率"
        for i in range(self.kpi_number):
            per_scores_lst = self.achievement_level[class_index][i].rates
            for j in per_scores_lst:
                if j is None:
                    continue
                yield j
        yield "课程目标达成值"
        for i in range(self.kpi_number):
            yield f"课程目标{i + 1}达成值"
            rates_lst = []
            for x in range(self.kpi_number):
                rates_lst.append(self.achievement_level[class_index][x].rates)
            lst = [f"{r}×{p}" for (_, p), r in zip(self.evaluation_stage, rates_lst[i]) if r is not None]
            total = sum(p for (_, p), r in zip(self.evaluation_stage, rates_lst[i]) if r is not None)
            yield "({})/{}={}".format(
                "+".join(lst),
                total if total != 1 else 1,
                self.achievement_level[class_index][i].achievement)
        yield "考核材料清单"

        y_str = (
            f"1.{self.evaluation_stage[0][0]}成绩：{'、'.join([x for x in self.n_evaluation_methods[:3] if x is not None])}；"
            f"2.{self.evaluation_stage[1][0]}成绩：{'、'.join([x for x in self.n_evaluation_methods[3:5] if x is not None])}；"
        )

        # 7.7
        if len(self.n_evaluation_methods) == 6 and self.n_evaluation_methods[5] is not None:
            y_str += f"3.{self.evaluation_stage[2][0]}成绩：{self.n_evaluation_methods[5]}"
        else:
            y_str += f"3.{self.evaluation_stage[2][0] if self.evaluation_stage[2][0] != '试卷' else '期末'}成绩：{self.evaluation_stage[2][0]}"

        yield y_str

        if len(self.class_list) == 1:
            yield "课程目标达成值"
            for i in range(self.kpi_number):
                yield self.achievement_level[0][i].achievement

            yield "课程目标达成结论"
            for i in range(self.kpi_number):
                if self.achievement_level[0][i].achievement >= 0.65:
                    yield "R达成（≥0.65）  £未达成（<0.65）"
                else:
                    yield "£达成（≥0.65）  R未达成（<0.65）"

        yield "结果分析"

        analysis_results = f"1.总体目标：本门课程有{self.kpi_number}个课程目标，"
        for index, p in enumerate(self.achievement_level[class_index]):
            analysis_results += f"课程目标{index + 1}达成值为{p.achievement}，"
            if index + 1 == self.kpi_number:
                analysis_results = analysis_results[:-1] + "。"
        for i in range(self.kpi_number):
            analysis_results += f"课程目标{i + 1}学生"
            if self.achievement_level[class_index][i].rates[0] is not None:
                analysis_results += \
                    (f"在{self.evaluation_stage[0][0]}环节中达到"
                     f"{get_rank(self.achievement_level[class_index][i].rates[0])}程度，")

            if self.achievement_level[class_index][i].rates[1] is not None:
                analysis_results += \
                    (f"在{self.evaluation_stage[1][0]}环节中"
                     f"表现{get_rank(self.achievement_level[class_index][i].rates[1])}，")

            if self.achievement_level[class_index][i].rates[2] is not None:
                analysis_results += \
                    (f"在{self.evaluation_stage[2][0] if self.evaluation_stage[2][0] != '试卷' else '考试'}"
                     f"环节中为{get_rank(self.achievement_level[class_index][i].rates[2])}水平，")

            analysis_results += f"总体目标{'达成' if self.achievement_level[class_index][i].achievement >= 0.65 else '未达成'}；"

        analysis_results = analysis_results[:-1] + "。"
        c_lst = [x.achievement for x in self.achievement_level[class_index]]
        min_index = min(enumerate(c_lst), key=lambda x: x[1])[0]
        analysis_results += f"分析{self.kpi_number}个课程目标的达成值发现，课程目标{min_index + 1}的达成情况较差。"
        analysis_results += "\n2.个体差异："
        for i in range(self.kpi_number):
            l, r = get_class_index_range(self.class_number, class_index)
            lst = min_score_people_name(self.stu_kpi_achieve_list, i, l, r)
            analysis_results += f"对于课程目标{i + 1}，"
            if self.achievement_level[class_index][i].rates[0] is not None:
                analysis_results += \
                    (f"{'、'.join(lst[0][:2])}{'等' if len(lst[0]) > 2 else ''}"
                     f"在{self.evaluation_stage[0][0]}考核中能力较弱，")
            if self.achievement_level[class_index][i].rates[1] is not None:
                analysis_results += \
                    (f"{'、'.join(lst[1][:2])}{'等' if len(lst[1]) > 2 else ''}"
                     f"在{self.evaluation_stage[1][0]}考核中能力较弱，")
            if self.achievement_level[class_index][i].rates[2] is not None:
                analysis_results += \
                    (f"{'、'.join(lst[2][:2])}{'等' if len(lst[2]) > 2 else ''}"
                     f"在{self.evaluation_stage[2][0] if self.evaluation_stage[2][0] != '试卷' else '考试'}中能力较弱；")
        analysis_results = analysis_results[:-1] + "。"
        yield analysis_results
        yield "改进措施"
        yield ("注：改进措施，包括课时分配、教材选用、教学方式、教学方法、教学内容、评分标准、过程评价及帮扶\n"
               f"{self.suggestion_template_list[0] if self.suggestion_template_list[0] is not None else '\n\n\n在这填入您的改进措施\n\n\n'}")
        while True:
            yield "如果您看到了本段文字，请联系开发者"

    def get_word_template_part_2(self):
        # self.get_word_template_part0()
        yield "课程目标"
        for i in range(self.kpi_number):
            yield f"课程目标{i + 1} （{self.hml_list[i]}）"
        yield ("总成绩=\n{}".
        format(
            "\n+".join([f"{n if n != '试卷' else '期末'}成绩×{int(p * 100)}%" for n, p in self.evaluation_stage if
                        p is not None])))
        for i in self.course_objectives:
            yield i
        yield "支撑毕业要求指标点"
        for i in self.kpi_list:
            yield i
        yield "考核类型"
        for i in range(self.kpi_number):
            s_lst = self.achievement_level[0][i].scores
            for index, (j, s) in enumerate(zip(self.evaluation_stage, s_lst)):
                if s is None:
                    continue
                if index == 2:
                    yield f"期末考核\n（{j[0]}）"
                else:
                    yield f"{j[0]}考核"

        yield "目标分值（分）"
        for i in self.target_score:
            for j in i:
                if j is None:
                    continue
                yield j
        yield "权重"
        for i in range(self.kpi_number):
            s_lst = self.achievement_level[0][i].scores
            for j, s in zip(self.evaluation_stage, s_lst):
                if s is None:
                    continue
                yield j[1]
        min_rates = []
        for index, i in enumerate(self.class_list):
            yield i
            for a_index, j in enumerate(self.achievement_level[index]):
                if len(min_rates) - 1 < a_index:
                    min_rates.append(j.achievement)
                else:
                    min_rates[a_index] = min(min_rates[a_index], j.achievement)
                yield j.achievement
        yield "课程目标达成值\n（取各班级最小值）"
        for i in min_rates:
            yield i
        yield "课程目标达成结论"
        for i in min_rates:
            if i >= 0.65:
                yield "R达成（≥0.65）  £未达成（<0.65）"
            else:
                yield "£达成（≥0.65）  R未达成（<0.65）"
        yield "结果分析"
        analysis_results = f"1.总体情况：\n    本门课程有{self.kpi_number}个课程目标，"
        for i in range(self.kpi_number):
            analysis_results += f"课程目标{i + 1}达成值为{min_rates[i]}，"
        analysis_results = analysis_results[:-1] + "。"
        passed_c_lst = [f"课程目标{index + 1}" for index, x in enumerate(min_rates) if x >= 0.65]
        not_passed_c_lst = [f"课程目标{index + 1}" for index, x in enumerate(min_rates) if x < 0.65]
        if len(passed_c_lst) > 0:
            analysis_results += (f"{'、'.join(passed_c_lst)}达到0.65的达成标准，"
                                 f"表明学生在{'、'.join(passed_c_lst)}方面基本达到了本门课程提出的能力要求"
                                 f"{'；' if len(not_passed_c_lst) > 0 else '。'}")
        if len(not_passed_c_lst) > 0:
            analysis_results += (f"{'、'.join(not_passed_c_lst)}未达到0.65的达成标准，"
                                 f"表明学生在{'、'.join(not_passed_c_lst)}方面未能达到课程提出的能力要求，"
                                 f"学生能力还有待进一步加强。")
        analysis_results += "\n2.班级差异："
        avg_c_lst = [sum(i.achievement for i in x) / len(x) for x in self.achievement_level]
        top_index = max(enumerate(avg_c_lst), key=lambda x: x[1])[0]
        bottom_index = min(enumerate(avg_c_lst), key=lambda x: x[1])[0]
        analysis_results += (f"\n    （1）班级对比：各班级之间比较而言，"
                             f"{self.class_list[top_index]}班的课程目标达成值相对较高，"
                             f"{self.class_list[bottom_index]}班的课程目标达成值较低。")
        analysis_results += f"\n    （2）同班级不同课程目标比较：分析{self.kpi_number}个课程目标的达成值发现，"
        for i in range(len(self.class_list)):
            min_p_rate_index = min(enumerate(self.achievement_level[i]), key=lambda x: x[1].achievement)[0]
            o_c_str = [f'课程目标{x + 1}' for x in range(self.kpi_number) if x != min_p_rate_index]
            analysis_results += (f"{self.class_list[i]}班的课程目标{min_p_rate_index + 1}达成情况最低，"
                                 f"达成值为{self.achievement_level[i][min_p_rate_index].achievement}，"
                                 f"{'、'.join(o_c_str)}的达成情况较好；")
        analysis_results = analysis_results[:-1] + "。"
        analysis_results += ("\n3.结果分析： \n"
                             f"{self.suggestion_template_list[2] if self.suggestion_template_list[2] is not None else '\n\n\n在此填写您的结果分析\n\n\n'}")
        yield analysis_results
        yield "改进措施"
        yield ("注：改进措施，包括课时分配、教材选用、教学方式、教学方法、教学内容、评分标准、过程评价及帮扶\n"
               f"{self.suggestion_template_list[1] if self.suggestion_template_list[1] is not None else '\n\n\n在这填入您的改进措施\n\n\n'}")
        while True:
            yield "如果您看到了本段文字，请联系开发者"

    def get_word_template_part_3(self):
        yield "课程目标达成情况合理性评价"
        yield "评价样本的合理性"
        yield "R全体样本  £抽样样本"
        yield "评价依据的合理性"
        yield "考核方法        R合适       £不合适"
        yield "考核内容是否支撑课程目标  R是   £否"
        yield "评分标准    R明确   £不明确"
        yield "计算过程的合理性"
        yield "R合理正确  £合理但不够准确 £不合理"
        yield "问题分析的合理性"
        yield "R合理深入  £合理但不够深入 £不合理"
        yield "改进措施的合理性"
        yield "R合理可行  £合理但不太可行 £不合理"
        yield "合理性评价结果"
        yield "R合理      £基本合理       £不合理"
        yield "专业负责人/系主任（签字）"
        yield ("整改意见：\n"
               "\n\n\n"
               f"{" " * 8}{self.suggestion_template_list[3] if self.suggestion_template_list[3] is not None else '\n\n\n'}\n\n\n")
        yield ""
        yield "签字："
        yield ""
        yield "日期："
        yield datetime.datetime.now().strftime("%Y-%m-%d")
        yield "课程负责人（签字）"
        yield ("拟整改计划与措施：\n"
               "\n\n\n"
               f"{" " * 8}{self.suggestion_template_list[4] if self.suggestion_template_list[4] is not None else '\n\n\n'}\n\n\n")
        yield ""
        yield "签字："
        yield ""
        yield "日期："
        yield datetime.datetime.now().strftime("%Y-%m-%d")
        while True:
            yield "如果您看到了本段文字，请联系开发者"


if __name__ == '__main__':
    excel_reader = ExcelReader('../files/21工管-房屋建筑学-点名册-1227.xlsm')
    excel_reader.parse_excel()
    s, e = get_class_index_range(excel_reader.class_number, 1)
    r = min_score_people_name(excel_reader.stu_kpi_achieve_list, 0, s, e)
    gen_picture(excel_reader.stu_kpi_achieve_list, 3, s, e)
    print()
