# -*- coding: utf-8 -*-
"""《软件测试课程设计》教学基础文件生成器 v2（字段与内容对齐源包《软件项目管理教学基础文件》）
输出：docx/ 与 md/（md 为可复制版：段落不换行）
"""
import os
from docx import Document
from docx.shared import Pt, Cm
from docx.oxml.ns import qn

BASE = os.path.dirname(os.path.abspath(__file__))
DOCX_DIR = os.path.join(BASE, 'docx'); MD_DIR = os.path.join(BASE, 'md')

# 知识点结构化讲稿（由 14 章课件正文抽取并按知识节归类，见 _讲稿结构化.json）
import re
import json as _json
try:
    SECTIONS = _json.load(open(os.path.join(BASE, '_讲稿结构化.json'), encoding='utf-8'))
except Exception:
    SECTIONS = {}
try:
    from _keyoutline import KEY_OUTLINE      # 手写知识点大纲（优先使用）
except Exception:
    KEY_OUTLINE = {}
try:
    from _szdesign import SZ_DESIGN           # 课程思政结构化设计（维度/载体/实施/评价）
except Exception:
    SZ_DESIGN = {}


def _clean(t):
    t = t.replace('\n', ' ').replace('\r', ' ')
    t = t.replace('版权所有', '').replace('仅限于教学使用', '').replace('感谢聆听', '')
    t = t.replace('同济大学', '').replace('朱少民', '').replace('软件测试方法和技术', '本课程教材')
    t = ' '.join(t.split())
    return t.strip(' 　,、；;·-—')


_NOISE = ('本课程教材', '本章导入', '版权所有', '感谢聆听', '点击', '单击', '动画')


def _shorten(c, n=46):
    """过长的要点按空格截到 n 字左右，保持短语形式。"""
    if len(c) <= n:
        return c
    buf = ''
    for seg in c.split(' '):
        if buf and len(buf) + len(seg) + 1 > n:
            break
        buf = (buf + ' ' + seg).strip()
    return buf or c[:n]


def _points(paras, limit=5):
    """把知识节正文提炼为要点短语（去页码/去目录页/去重复/限长）。"""
    out = []
    for p in paras:
        for c in re.split(r'[；。！？]', _clean(p)):
            c = re.sub(r'^\d+(\.\d+)*\s*[、\.．]?\s*', '', c).strip()
            c = re.sub(r'^[（(]?[一二三四五六七八九十\d]+[）)\s、\.]+', '', c).strip()
            c = re.sub(r'\s+\d{1,4}$', '', c).strip()
            if any(k in c for k in _NOISE):
                continue
            cjk = len(re.findall(r'[\u4e00-\u9fff]', c))
            if cjk < 4 or c in out:
                continue
            if len(re.findall(r'\d', c)) >= 4:              # 数字噪声（版本号/指标值堆砌）
                continue
            if len(re.findall(r'\d+\.\d+', c)) >= 2:      # 目录页：7.2 安全性 7.3 兼容性 …
                continue
            if re.fullmatch(r'[\d\s%％\.\-–—/]+', c):
                continue
            c = _shorten(c)
            if 6 <= len(c) <= 70 and c not in out:
                out.append(c)
    return out[:limit]


def chapter_outline(s):
    """按课程模板形式给出本次课的知识点大纲：编号知识点 ＋ 要点条目。"""
    hand = KEY_OUTLINE.get(s['no'])
    if hand:
        blocks = []
        for i, (title, pts) in enumerate(hand, 1):
            blocks.append(('p', f"{i}. {_clean(title)}"))
            blocks += [('l1', _clean(x)) for x in pts]
        return blocks
    ch = s['chapter']
    m = re.match(r'第(\d+)章', ch)
    if not m:
        return []
    secs = SECTIONS.get(m.group(1)) or []
    if not secs:
        return []
    if s['chapter'] == '第3章':          # 第3章分两次课：按知识节对半
        half = (len(secs) + 1) // 2
        secs = secs[:half] if s['no'] == 3 else secs[half:]
    blocks, i = [], 0
    for sec in secs:
        st = _clean(sec['title'])
        if any(k in st for k in _NOISE):        # 目录页/版权页等非知识节
            continue
        pts = _points(sec['paras'])
        if not pts:
            continue
        i += 1
        title = re.split(r'[？?]', _clean(sec['title']))[0].strip()[:30] or f"知识要点 {i}"
        blocks.append(('p', f"{i}. {title}"))
        blocks += [('l1', x) for x in pts]
    return blocks

C = dict(
    name='软件测试课程设计', en='Software Testing Course Design',
    code='154442008', nature='专业选修', ctype='专业课程 / 实践课程',
    hours=32, credits=2, theory=16, practice=16,
    college='计算机学院', teacher='周宇文', phone='13580390715',
    reviewer='周瑞红', date='2026-09-13', lang='中文',
    major='计算机科学与技术、软件工程、数据科学与大数据技术',
    classes='2024/2025 级计算机类相关专业本科生（以教务选课名单为准）',
    prereq='程序设计基础、软件工程、数据库原理',
    textbook='朱少民. 软件测试方法和技术（第 4 版）[M]. 北京：清华大学出版社，2022.',
    exam='考查（过程性考核＋课程设计作品与答辩）',
    aim='学生通过对本课程的学习，掌握软件测试的基本理论、方法、技术与常用工具，熟悉软件测试的流程与规范；能够针对具体软件系统开展测试需求分析、测试计划制定、测试用例设计与执行、缺陷跟踪与测试报告撰写；培养规范化、工程化的测试实践能力与团队协作能力，并在课程中融入工程伦理与职业道德教育，培养学生的质量意识、诚信意识、团队精神、社会责任感和科技报国的情怀。',
    cond='要求学生先修程序设计基础、软件工程、数据库原理等课程，具备一定的编程与调试能力，了解软件开发生命周期。',
    content=[
        '1. 软件测试基础：软件测试的必要性、基本概念、学科形成、正反思维与测试定义，测试与质量保证、与开发的关系；',
        '2. 软件测试方法与流程：基于直觉与经验的测试、基于模型与形式的测试，传统测试过程与敏捷测试过程、测试规范与过程改进；',
        '3. 软件测试技术：单元测试与集成测试、系统功能测试、专项测试（性能/安全/兼容/可靠性/易用性）、软件本地化测试；',
        '4. 测试自动化与工程化：测试自动化原理与框架、测试需求分析与测试计划、测试用例设计与维护、测试基础设施与缺陷跟踪、测试执行与测试报告；',
        '5. 课程设计项目实践：以一个具体软件系统为对象，完成从测试分析、用例设计、测试执行到缺陷报告与测试总结的完整实践，并进行项目答辩。',
    ],
    refs=[
        '朱少民. 全程软件测试（第 3 版）[M]. 北京：人民邮电出版社，2019.',
        '朱少民. 致命 Bug——软件缺陷的灾难与启示[M]. 北京：人民邮电出版社，2016.',
        '朱少民. 软件测试知识地图[Z]. 2022.',
        '陈能技. 软件测试技术大全：测试基础、流行工具、项目实战[M]. 北京：人民邮电出版社，2008.',
        'Myers G J, Sandler C, Badgett T. The Art of Software Testing[M]. 3rd ed. Hoboken: John Wiley & Sons, 2011.',
        'Kaner C, Bach J, Pettichord B. Lessons Learned in Software Testing[M]. New York: John Wiley & Sons, 2001.',
        'Crispin L, Gregory J. Agile Testing: A Practical Guide for Testers and Agile Teams[M]. Boston: Addison-Wesley, 2008.',
        'Meszaros G. xUnit Test Patterns: Refactoring Test Code[M]. Boston: Addison-Wesley, 2007.',
        'Whittaker J A. Exploratory Software Testing[M]. Boston: Addison-Wesley, 2009.',
        'Beck K. Test-Driven Development: By Example[M]. Boston: Addison-Wesley, 2002.',
        'Humble J, Farley D. Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation[M]. Boston: Addison-Wesley, 2010.',
        'Fowler M. 重构：改善既有代码的设计（第 2 版）[M]. 北京：人民邮电出版社，2019.',
        'Koomen T, Pol M. TMap NEXT: for Result-driven Testing[M]. Den Bosch: UTN Publishers, 2006.',
        'ISO/IEC/IEEE 29119 Software and Systems Engineering — Software Testing[S]. Geneva: ISO, 2013—2021.',
        'GB/T 15532—2008 计算机软件测试规范[S]. 北京：中国标准出版社，2008.',
        'GB/T 25000.10—2016 系统与软件工程 系统与软件质量要求和评价（SQuaRE）第 10 部分：系统与软件质量模型[S]. 北京：中国标准出版社，2016.',
        '中国证券监督管理委员会. 证券期货业软件测试规范[S]，2020.',
        '2021 年国内软件质量调查报告[R]. 2021.',
        'ISTQB. Certified Tester Foundation Level Syllabus[EB/OL]. https://www.istqb.org/.',
        'Apache JMeter 官方文档[EB/OL]. https://jmeter.apache.org/.',
        'Postman 官方学习中心[EB/OL]. https://learning.postman.com/.',
        'Playwright 官方文档[EB/OL]. https://playwright.dev/.',
        'Selenium 官方文档[EB/OL]. https://www.selenium.dev/documentation/.',
        'TestLink 官方文档[EB/OL]. https://testlink.org/.',
        'SonarQube 官方文档[EB/OL]. https://docs.sonarsource.com/sonarqube/.',
    ],
    courses_taught='《Java Web 程序设计》、《大学计算机高级应用》、《软件质量控制与测试》、《计算机导论》、《大学计算机基础应用》、《Python 程序设计基础》',
    courses_now='《软件测试课程设计》、《软件测试》、《软件项目管理》',
)

SESSIONS = [
 dict(no=1, chapter='第1章', title='课程导学与引论：为什么必须做软件测试', method='多媒体讲授＋案例分析', support='课程目标1',
   goal=['理解软件缺陷与质量成本的概念', '理解软件测试的必要性与价值', '掌握软件测试的基本定义', '了解本课程的考核方式与课程设计任务'],
   key=['软件缺陷的代价与质量成本观', '软件测试的必要性（真实事故案例）', '软件测试的定义与学科形成'],
   hard=['区分测试、调试、质量保证三者的边界', '理解测试左移与成本非线性增长'],
   points=['课程导学：学时、考核、教材与课程设计任务', '1.1 软件测试的必要性', '1.2 为什么要进行软件测试', '1.3 什么是软件测试（学科形成、正反思维、定义）', '1.4–1.6 测试与 SQA、与开发的关系、TDD 思想'],
   detail=['以《狮子王》安装失败、Intel 奔腾浮点错误、火星探测器与波音星际客机、骑士资本、AWS S3 宕机、Therac-25 放疗仪等真实事故说明：软件缺陷客观存在，且代价可能极其高昂；测试是发布前的质检关卡。',
           '以国产游戏《血狮》（1997）为例，剖析宣传远超前于被验证的产品所带来的后果：大量用户无法安装运行、兼容性差、功能与宣传不符，最终成为国产软件史上的反面教材。',
           '给出软件测试的权威定义：在指定条件下运行或评审系统与组件，观察或记录结果，对照预期作出评价；并说明测试包含静态测试与动态测试。',
           '介绍测试与 SQA、与开发的关系（并行协作、测试左移）以及 TDD 测试在前、开发在后的基本思想。'],
   case='结合《血狮》案例讨论：如果当年有规范的测试流程，哪些问题可能被提前发现？',
   org=['采用多媒体讲授，配合真实事故案例与现场提问。', '先让学生猜测事故损失量级，再给出真实数据，形成认知冲击。', '通过宣传与现实的对比，引导学生建立发布前必须验证的意识。', '最后给出本课程主线：原理方法→测试技术→项目实践。'],
   hw=['整理 5 个真实软件缺陷事故，标注缺陷类型（兼容/精度/集成/日期/数据等）。', '用自己的话写出软件测试的定义，并说明它与调试、质量保证的区别。'],
   practice=['安装并熟悉课程实验环境（浏览器、截图工具、待测小程序）。', '完成第一次测试体验：找出给定小程序中至少 2 个缺陷并截图记录。'],
   sz='从国产软件《血狮》的教训切入，讨论工匠精神与质量意识，引导学生理解严谨、负责的职业态度。'),
 dict(no=2, chapter='第2章', title='软件测试的基本概念', method='多媒体讲授＋课堂练习', support='课程目标1、2',
   goal=['掌握软件测试的分类与测试级别', '理解静态测试与动态测试的区别', '理解黑盒、白盒与灰盒测试', '了解软件测试工作范畴与岗位分工'],
   key=['测试分类与测试级别', '静态测试与动态测试', '黑盒测试与白盒测试'],
   hard=['不同测试级别的目标与执行主体', '黑盒与白盒方法的选择依据'],
   points=['测试分类与测试级别', '静态测试和动态测试', '主动测试和被动测试', '黑盒测试和白盒测试', '软件测试工作范畴'],
   detail=['按是否运行程序可分为静态测试与动态测试：评审、走查、静态分析属于静态测试；执行用例、观察结果属于动态测试。',
           '按对内部结构的了解程度可分为黑盒、白盒与灰盒测试；黑盒关注输入输出与业务规则，白盒关注代码结构与路径覆盖，灰盒兼顾两者。',
           '测试级别包括单元测试、集成测试、系统测试与验收测试，各阶段的对象、目标与执行主体不同，构成完整的测试链。',
           '软件测试工作范畴包括测试分析、测试设计、测试执行、缺陷管理、测试环境与工具建设等，对应不同的岗位分工。'],
   case='同一段代码缺陷：用黑盒视角与白盒视角分别能发现什么？',
   org=['以同一功能、不同视角的对比讲解测试分类。', '现场演示一次静态审查与一次动态执行的区别。', '组织学生对给定功能点分类归属进行快速抢答。'],
   hw=['为“学生选课系统”分别列出适合黑盒与白盒测试的功能点。', '画出单元—集成—系统—验收四级测试的关系图。'],
   practice=['使用等价类与边界值思路，为登录功能设计 10 条用例。'],
   sz='通过测试级别的分工讲解团队协作与责任边界，培养规范意识。'),
 dict(no=3, chapter='第3章', title='软件测试方法（一）：基于直觉与经验', method='讲授＋探索式测试实践', support='课程目标2',
   goal=['掌握错误猜测法与探索式测试', '理解基于缺陷模式的测试思想', '能根据场景选择测试方法'],
   key=['错误猜测法与探索式测试', '基于缺陷模式的测试（DPBT）', '测试方法分类框架'],
   hard=['探索式测试的会话组织与记录', '把个人经验转化为可复用用例'],
   points=['软件测试方法分类', '错误猜测法', '探索式测试', '基于缺陷模式的测试'],
   detail=['错误猜测法依赖测试者对易错点的经验判断，快速列出高风险场景，适合时间受限的探索阶段。',
           '探索式测试强调学习、设计、执行同步进行，通过会话（Session）组织，配合时间盒与记录，兼顾灵活性与可追溯性。',
           '缺陷模式把历史缺陷抽象为可识别的模式（如空指针、资源未释放、边界越界），据此设计针对性用例并用于缺陷预防。'],
   case='针对“文件上传”功能，用错误猜测法列出 10 个可能的出错点。',
   org=['先由学生自由列举易错点，教师归纳为分类框架。', '用真实缺陷案例说明经验为什么有效、也会失效。', '组织一次短时探索式测试，现场记录发现。'],
   hw=['查找并阅读一个缺陷模式（如空指针、资源未释放），说明如何在测试中触发它。'],
   practice=['以小组为单位开展 30 分钟探索式测试会话，记录测试思路与发现。'],
   sz='强调批判性思维与实事求是的科学态度，鼓励质疑与求证。'),
 dict(no=4, chapter='第3章', title='软件测试方法（二）：基于模型与形式', method='讲授＋建模练习', support='课程目标2',
   goal=['理解基于模型测试的原理与流程', '能用状态机模型生成测试用例', '了解形式化测试方法及其适用场景'],
   key=['基于模型的测试（MBT）', '形式化测试方法', '测试方法的选择与组合'],
   hard=['模型抽象与状态迁移覆盖准则', '方法成本与收益的权衡'],
   points=['基于模型的测试', '形式化测试方法', '测试方法的选择策略'],
   detail=['基于模型的测试从需求或设计抽取状态机、流程图、决策表等模型，由模型自动或半自动生成覆盖各迁移与条件的用例。',
           '形式化方法以严格的数学语义描述系统行为与测试条件，适用于安全关键系统，代价是建模与验证成本较高。',
           '方法选择应结合需求特点、风险等级、时间与人员能力综合权衡，实际项目常采用多种方法组合。'],
   case='把一个登录—锁定—解锁业务流程画成状态机，并生成覆盖各迁移的用例。',
   org=['以状态机建模为主线，边讲边画。', '学生分组完成模型并互评覆盖是否完整。', '对比不同方法在同一需求上的用例数量与质量。'],
   hw=['为给定的订单状态机设计覆盖全部迁移的测试路径。'],
   practice=['使用状态转换图完成 15 条用例设计，并说明覆盖准则。'],
   sz='从模型与验证的严谨性延伸到工程规范的遵守与精益求精。'),
 dict(no=5, chapter='第4章', title='软件测试流程和规范', method='讲授＋流程设计练习', support='课程目标1、4',
   goal=['理解传统测试过程与敏捷测试过程', '掌握测试准入准出条件', '了解测试规范与过程改进'],
   key=['传统测试过程（V 模型）', '敏捷测试与测试左移', '测试规范与过程改进'],
   hard=['敏捷迭代中的测试节奏安排', '准入准出条件的量化'],
   points=['传统的软件测试过程', '敏捷测试过程（含 SBTM）', '软件测试学派与测试规范', '测试过程改进'],
   detail=['传统测试过程强调阶段划分与文档化，需求、设计、编码、测试依次推进，便于评审与追溯。',
           '敏捷测试强调持续测试、自动化回归与团队共责，测试与开发并行，测试左移、快速反馈。',
           '测试规范明确各阶段的输入输出、评审要求与准入准出条件；测试过程改进用于持续提升测试效能。'],
   case='同一个需求变更：瀑布流程与敏捷流程下测试工作分别如何组织？',
   org=['对比两种流程的时间线与角色分工。', '让学生为本组项目设计一页式测试流程。', '讨论没有文档算不算测试规范这一常见争议。'],
   hw=['绘制本课程项目的测试流程（含准入准出条件）。'],
   practice=['为本组课程设计项目制定一页式测试计划与流程。'],
   sz='以规范即责任讨论工程纪律对软件质量与安全的意义。'),
 dict(no=6, chapter='第5章', title='单元测试与集成测试', method='讲授＋JUnit 实验', support='课程目标2、3',
   goal=['掌握单元测试的目标与用例设计方法', '能使用 JUnit 等框架编写并运行测试', '理解集成策略与持续集成'],
   key=['单元测试目标与用例设计', 'JUnit 框架使用', '集成策略与 CI'],
   hard=['测试替身（Mock/Stub）的使用', '接口覆盖与集成顺序'],
   points=['单元测试的目标和任务', '动态测试与 JUnit 工具使用', '分层单元测试', '集成测试与 CI/CD'],
   detail=['单元测试针对最小可测单元，强调独立、可重复、自动化；通过断言验证结果，通过测试替身隔离外部依赖。',
           'JUnit 提供注解、断言、参数化测试与测试套件，可结合覆盖率工具评估测试充分性。',
           '集成测试关注模块间接口与协作，常见策略有大爆炸、自顶向下、自底向上与三明治集成，需根据依赖关系选择。',
           '将单元测试接入持续集成流水线，可在每次提交后自动回归，快速暴露集成缺陷。'],
   case='为什么模块单测都通过，系统仍然出错？结合火星探测器接口案例讨论。',
   org=['现场演示 JUnit 测试的编写—运行—失败—修复闭环。', '让学生观察覆盖率报告并讨论覆盖率高的测试是否一定好。', '以航天事故说明接口测试的重要性。'],
   hw=['为给定 Java 方法编写单元测试，覆盖正常与异常分支。'],
   practice=['在 IDE 中运行 JUnit 测试并查看覆盖率；把测试接入一次本地 CI 流程。'],
   sz='从航天事故谈接口即责任，培养严谨细致的工程作风。'),
 dict(no=7, chapter='第6章', title='系统功能测试', method='讲授＋接口/UI 测试实验', support='课程目标2、3',
   goal=['掌握功能测试的思路与方法', '能设计端到端功能用例', '了解 UI/API 自动化与回归测试'],
   key=['功能测试思路与方法', 'UI 与 API 功能自动化', '回归测试与精准测试'],
   hard=['端到端用例的粒度与维护', '自动化脚本稳定性'],
   points=['功能测试思路与方法', '（UI、API）功能测试自动化', '回归测试', '精准测试'],
   detail=['功能测试从业务流程出发设计端到端用例，覆盖正常流程、异常流程与边界条件。',
           'UI 自动化适合稳定且高频的核心流程；API 自动化执行快、维护成本低，适合作为自动化主体。',
           '回归测试在每次变更后验证既有功能；精准测试通过变更影响分析缩小回归范围，提高效率。'],
   case='电商下单流程中，哪些环节适合 UI 自动化、哪些更适合 API 自动化？',
   org=['以业务流程为主线梳理测试点。', '现场演示接口测试与 UI 测试的执行差异。', '组织学生对自动化收益进行估算讨论。'],
   hw=['为“购物车结算”设计 8 条端到端功能用例。'],
   practice=['使用 Postman 或 JMeter 完成一组接口功能测试并保存测试集。'],
   sz='结合国产电商与支付系统的质量要求，讨论可靠性与用户信任。'),
 dict(no=8, chapter='第7章', title='专项测试：性能、安全、兼容与易用性', method='讲授＋JMeter 压测实验', support='课程目标2、3',
   goal=['掌握性能测试指标与场景设计', '了解安全测试基本方法', '掌握兼容性、可靠性与易用性测试要点'],
   key=['性能测试指标与场景', '安全测试方法', '兼容性与易用性测试'],
   hard=['性能瓶颈定位', '安全测试的边界与合规'],
   points=['性能测试', '安全性测试', '兼容性测试', '可靠性测试', '易用性测试'],
   detail=['性能测试关注响应时间、吞吐量、并发用户数与资源占用，通过场景设计与监控发现瓶颈。',
           '安全测试关注认证授权、输入校验、敏感数据保护与越权访问，需在法律与授权范围内开展。',
           '兼容性测试覆盖浏览器、操作系统、设备与分辨率差异；可靠性测试关注长时间运行与异常恢复；易用性测试关注用户操作效率与体验。'],
   case='Uber 隐私泄露与 AWS 宕机：分别属于哪类专项测试范围？',
   org=['以指标、场景、工具、分析四步讲解性能测试。', '演示 JMeter 脚本与结果图表解读。', '强调安全测试的合规边界。'],
   hw=['为一个 Web 系统列出性能测试场景与监控指标。'],
   practice=['使用 JMeter 对给定接口做并发压测，记录并解释结果曲线。'],
   sz='通过隐私保护与数据安全案例，强化法律意识与职业伦理。'),
 dict(no=9, chapter='第8章', title='软件本地化测试', method='讲授＋本地化检查实践', support='课程目标2、3',
   goal=['理解本地化的技术问题', '掌握本地化功能与界面测试方法', '能识别多语言与区域设置缺陷'],
   key=['本地化技术问题', '本地化功能与界面测试', '多语言与区域设置'],
   hard=['编码与排版差异引发的缺陷', '时区、货币、日期格式处理'],
   points=['本地化测试概述', '本地化测试的技术问题', '本地化的功能测试', '多语言与区域设置验证'],
   detail=['本地化缺陷多与字符编码、字体显示、文本长度与布局方向有关，必须在目标语言环境下验证。',
           '日期、时间、时区、货币与度量单位等区域设置错误，是国际化软件最常见的缺陷来源之一。',
           '本地化测试既包括界面文案与功能验证，也包括数据格式、排序规则与文化适配。'],
   case='把 iPhone 日期设置为 1970-01-01 会变砖，这属于什么类型的缺陷？',
   org=['对比同一界面在中英文环境下的差异截图。', '演示区域设置引起的日期与货币格式问题。', '让学生动手切换语言与区域做检查。'],
   hw=['列举 5 个本地化/国际化常见缺陷并说明验证方法。'],
   practice=['切换系统语言与区域，对给定应用做一次本地化检查并提交缺陷清单。'],
   sz='从软件走出去谈文化尊重与国际化视野。'),
 dict(no=10, chapter='第9章', title='测试自动化及其框架', method='讲授＋自动化环境搭建', support='课程目标2、3',
   goal=['理解测试自动化的内涵与适用条件', '了解常见自动化框架类型', '能搭建最小可运行的自动化用例'],
   key=['自动化内涵与实现原理', '自动化框架类型', '自动化实施与维护'],
   hard=['框架选型与分层设计', '自动化投入产出评估'],
   points=['测试自动化的内涵', '测试自动化实现原理', '测试自动化的实施', 'API 与移动应用自动化框架'],
   detail=['自动化的价值在于可重复执行与快速反馈，适合稳定、高频、结果可判定的场景。',
           '常见框架包括数据驱动、关键字驱动、行为驱动（BDD）与混合框架，分层设计可降低维护成本。',
           '自动化实施需评估投入产出、选择合适工具、建立脚本规范与持续维护机制，避免脚本腐化。'],
   case='什么情况下不适合做自动化？请举例说明。',
   org=['以投入产出为核心讨论自动化的价值与边界。', '演示一个最小自动化脚本从编写到运行。', '让学生调研不同工具的适用场景。'],
   hw=['调研一款自动化测试工具，写出选型对比（至少 3 个维度）。'],
   practice=['搭建 Selenium/Playwright 环境，完成一个最小可运行的自动化用例。'],
   sz='从工具替代重复劳动讨论技术效率与持续学习能力。'),
 dict(no=11, chapter='第10章', title='测试需求分析与测试计划', method='讲授＋测试计划编写', support='课程目标2、4',
   goal=['掌握测试需求分析方法', '能把业务需求转化为可测指标', '能编写测试计划'],
   key=['测试需求分析方法', '测试目标与准则', '测试估算与进度安排'],
   hard=['需求到测试点的转化', '工作量估算与风险识别'],
   points=['测试的目标和准则', '测试需求分析', '测试项目的估算与进度安排', '测试计划编写'],
   detail=['测试需求分析把业务需求拆解为可验证的测试点，并识别需求歧义、遗漏与不可测之处。',
           '测试计划明确测试范围、策略、资源、进度、风险与交付物，是后续测试工作的基线。',
           '测试估算可采用类比、经验与工作量分解等方法，并结合风险确定优先级与资源分配。'],
   case='需求文档里“系统应快速响应”如何转化为可测试的指标？',
   org=['以一条模糊需求为例，现场演练细化为可测指标。', '展示测试计划的完整目录结构。', '组织学生对计划的风险项进行补充。'],
   hw=['为课程设计题目编写测试需求清单与测试计划（1–2 页）。'],
   practice=['评审一份真实需求说明，输出测试点与测试计划初稿。'],
   sz='从需求不清导致返工谈沟通责任与契约精神。'),
 dict(no=12, chapter='第11章', title='设计和维护测试用例', method='讲授＋用例设计与评审', support='课程目标2、4',
   goal=['掌握测试用例的构成要素', '能编写规范用例并组织评审', '理解用例维护与复用'],
   key=['测试用例构成与设计方法', '用例组织与管理', '用例评审与维护'],
   hard=['用例粒度与覆盖度的平衡', '用例库长期维护'],
   points=['测试用例的构成及其设计', '测试用例的组织与管理', '测试用例评审', '测试用例的维护与复用'],
   detail=['一条完整用例包含编号、标题、前置条件、测试数据、操作步骤与预期结果，缺一不可。',
           '用例设计方法包括等价类、边界值、判定表、场景法等，应结合需求特点组合使用。',
           '用例需要版本管理、评审与定期维护，随需求变更同步更新，避免用例与实际功能脱节。'],
   case='同一个功能，写 5 条用例与写 50 条用例，如何判断哪个更合适？',
   org=['展示好用例与差用例的对比样例。', '组织小组交叉评审并给出修改意见。', '演示测试管理工具中的用例组织方式。'],
   hw=['为“注册”功能编写 12 条规范测试用例并交叉评审。'],
   practice=['使用测试管理工具（如 TestLink）录入用例并执行一轮。'],
   sz='以文档即证据强调规范记录与可追溯性。'),
 dict(no=13, chapter='第12章', title='部署测试基础设施', method='讲授＋环境与缺陷流程演练', support='课程目标3、4',
   goal=['理解测试基础设施的组成', '能准备测试环境与测试数据', '掌握缺陷跟踪流程'],
   key=['测试环境与数据管理', '测试工具链与持续集成', '缺陷跟踪与配置管理'],
   hard=['环境一致性保障', '测试数据的构造与脱敏'],
   points=['测试基础设施概述', '测试环境与数据准备', '持续集成与流水线', '缺陷跟踪与配置管理'],
   detail=['环境、数据、工具与流水线共同构成测试基础设施；环境不一致是本地能跑、线上不行的根源。',
           '测试数据需要覆盖正常、异常与边界场景，敏感数据必须脱敏，避免隐私与合规风险。',
           '缺陷跟踪流程包括提交、分派、修复、验证与关闭，需与版本管理、需求变更联动，形成可追溯记录。'],
   case='在我机器上是好的——如何用基础设施手段避免这类问题？',
   org=['以 Docker 为例演示环境一致性。', '展示缺陷生命周期与状态流转图。', '组织一次缺陷提交与验证的模拟演练。'],
   hw=['画出本组项目的测试环境与缺陷处理流程图。'],
   practice=['搭建 Docker 测试环境；在缺陷跟踪工具中完成一次缺陷全流程演练。'],
   sz='从环境一致性谈标准化与团队协作效率。'),
 dict(no=14, chapter='第13章', title='测试执行与结果评估、报告', method='讲授＋测试报告撰写', support='课程目标3、4',
   goal=['掌握测试执行的组织方式', '能编写规范的缺陷报告', '能完成测试结果评估与测试报告'],
   key=['测试执行与准入准出', '缺陷报告规范', '测试结果评估与报告'],
   hard=['缺陷严重程度与优先级判定', '质量评估与发布建议'],
   points=['测试执行流程', '缺陷报告与跟踪', '测试结果评估与度量', '测试报告编写与发布建议'],
   detail=['测试执行需按计划推进，记录执行结果与阻塞问题，并遵循准入准出条件。',
           '缺陷报告要可复现：环境、前置条件、操作步骤、测试数据、实际结果与预期结果缺一不可。',
           '测试报告汇总用例覆盖率、缺陷分布、遗留风险与质量结论，给出是否可发布的建议，并用数据支撑结论。'],
   case='一个偶现缺陷如何写出高质量报告并推动修复？',
   org=['展示缺陷报告模板与反例。', '演练从执行记录到测试报告的汇总过程。', '讨论测试通过与可以发布的区别。'],
   hw=['编写一份完整测试报告（含覆盖率与缺陷分析）。'],
   practice=['执行一轮系统测试，提交缺陷并输出测试报告。'],
   sz='从数据说话谈客观评价与职业诚信。'),
 dict(no=15, chapter='第14章', title='软件测试展望与综合案例', method='讲授＋案例复盘', support='课程目标1、4',
   goal=['了解软件测试技术发展趋势', '了解测试岗位能力要求', '能够综合运用所学方法分析案例'],
   key=['测试技术发展趋势', '测试职业发展路径', '综合案例复盘'],
   hard=['新技术在测试中的应用边界', '从案例中提炼可迁移方法'],
   points=['软件测试展望', 'AI 助力软件测试', '测试职业能力与岗位要求', '综合案例复盘'],
   detail=['自动化、持续测试与智能化测试正在改变测试工作方式，但测试分析与质量判断仍是核心能力。',
           '测试岗位要求兼具测试基础、工具能力、业务理解与沟通协作能力，测试开发与质量保障方向日益细分。',
           '以真实案例复盘：从需求、设计、编码到发布，测试在每个环节可以做什么、应该做什么。'],
   case='AI 能替代测试人员吗？结合本章案例谈谈你的判断。',
   org=['以趋势、岗位、案例三段式展开。', '组织开放式辩论：AI 对测试岗位的影响。', '引导学生规划个人能力发展路径。'],
   hw=['调研一项测试新技术，写 500 字评述其对测试岗位的影响。'],
   practice=['完成课程设计项目自测，准备项目汇报材料。'],
   sz='从国产软件与开源生态谈技术自信与持续学习。'),
 dict(no=16, chapter='综合实践', title='课程设计项目汇报与答辩', method='汇报答辩＋教师点评', support='课程目标1–4',
   goal=['能够完整呈现测试方案与执行结果', '能够分析缺陷并给出质量结论', '能够进行工程表达与答辩'],
   key=['测试方案与用例设计展示', '测试执行结果与缺陷分析', '项目答辩与互评'],
   hard=['测试结论的合理性与证据完整度', '答辩表达与问题回应'],
   points=['项目汇报：测试对象、范围、策略、用例与执行结果', '缺陷分析与质量评估', '经验总结与改进建议', '教师点评与互评'],
   detail=['各组按统一模板汇报课程设计成果，重点说明测试策略、用例设计依据与缺陷发现过程。',
           '评分依据包括方案合理性、用例质量、执行严谨性、报告规范性与答辩表现。',
           '通过互评与教师点评，总结共性问题与改进方向，形成课程学习闭环。'],
   case='互评：如果你是项目经理，是否同意该项目发布？给出理由。',
   org=['每组限时汇报，组间提问与互评。', '教师按评分表打分并现场点评。', '汇总共性问题与优秀实践，形成课程总结。'],
   hw=['提交课程设计报告与测试资产（用例、脚本、缺陷记录、测试报告）。'],
   practice=['完成项目汇报答辩，提交全部课程设计成果物。'],
   sz='通过答辩与互评培养责任意识、表达沟通与团队协作精神。'),
]

WEEK_TABLE = [(str(s['no']), s['chapter'], s['title'], '2', s['method']) for s in SESSIONS] + \
             [('17', '—', '考核周：课程设计成果评定与成绩提交', '0', '考核/答辩收尾')]


def md_render(blocks):
    out = []
    for b in blocks:
        t = b[0]
        if t == 'h1': out.append('# ' + b[1])
        elif t == 'h2': out.append('\n## ' + b[1])
        elif t == 'h3': out.append('\n### ' + b[1])
        elif t == 'l1': out.append('- ' + b[1])
        elif t == 'p': out.append('\n' + b[1].replace('\r', ' ').replace('\n', ' '))
        elif t == 'table':
            rows = b[1]; out.append('')
            out.append('| ' + ' | '.join(str(x) for x in rows[0]) + ' |')
            out.append('|' + '|'.join(['---'] * len(rows[0])) + '|')
            for r in rows[1:]:
                out.append('| ' + ' | '.join(str(x).replace('|', '／') for x in r) + ' |')
    return '\n'.join(out).strip() + '\n'


def docx_add(doc, blocks):
    for b in blocks:
        t = b[0]
        if t == 'h1': doc.add_heading(b[1], level=0)
        elif t == 'h2': doc.add_heading(b[1], level=1)
        elif t == 'h3': doc.add_heading(b[1], level=2)
        elif t == 'p': doc.add_paragraph(b[1].replace('\n', ' '))
        elif t == 'l1': doc.add_paragraph(b[1], style='List Bullet')
        elif t == 'table':
            rows = b[1]; tb = doc.add_table(rows=0, cols=len(rows[0])); tb.style = 'Table Grid'
            for r in rows:
                cells = tb.add_row().cells
                for i, v in enumerate(r): cells[i].text = str(v)


def new_doc():
    d = Document(); st = d.styles['Normal']; st.font.name = '宋体'; st.font.size = Pt(11)
    st.element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    for s in d.sections:
        s.left_margin = Cm(2.2); s.right_margin = Cm(2.2); s.top_margin = Cm(2.0); s.bottom_margin = Cm(2.0)
    return d


def save(name, blocks):
    os.makedirs(DOCX_DIR, exist_ok=True); os.makedirs(MD_DIR, exist_ok=True)
    open(os.path.join(MD_DIR, name + '.md'), 'w', encoding='utf-8').write(md_render(blocks))
    d = new_doc(); docx_add(d, blocks); d.save(os.path.join(DOCX_DIR, name + '.docx'))
    print('已生成:', name)


def doc_简介():
    return [
        ('h1', '课程简介'),
        ('table', [
            ['项目', '内容'],
            ['课程名称', f"{C['name']}（{C['en']}）"],
            ['课程代码', C['code']],
            ['课程性质', C['nature']],
            ['总学时 / 学分', f"{C['hours']} 学时 / {C['credits']} 学分（理论 {C['theory']} ＋ 实践 {C['practice']}）"],
            ['开课学院', C['college']],
            ['适用专业', C['major']],
            ['授课对象', C['classes']],
            ['任课教师', f"{C['teacher']}（电话/微信：{C['phone']}）"],
            ['开设目的', C['aim']],
            ['修读条件', C['cond']],
            ['主要内容', ' '.join(C['content'])],
            ['教材', C['textbook']],
            ['参考书', ' '.join(C['refs'][:12])],
            ['考核方式', C['exam']],
        ]),
        ('h2', '一、课程性质与目的'),
        ('p', f"《{C['name']}》是面向计算机类专业开设的{C['nature']}课程，共 {C['hours']} 学时、{C['credits']} 学分，安排在第 1–17 周（含实验）。课程以软件测试方法与技术为主线，采用讲授与实验相结合的方式，围绕会设计、会执行、会分析、会报告组织教学，边学边练。"),
        ('p', C['aim']),
        ('h2', '二、修读条件'),
        ('p', C['cond']),
        ('h2', '三、主要内容'),
    ] + [('l1', x) for x in C['content']] + [
        ('h2', '四、教材与参考书'),
        ('l1', '使用教材：' + C['textbook']),
    ] + [('l1', '参考书：' + r) for r in C['refs']] + [
        ('h2', '五、考核方式'),
        ('p', '本课程为考查类课程，总评成绩由过程性考核与课程设计成果构成：考勤与课堂表现 10%、实验与阶段任务 40%、课程设计作品与答辩 50%。具体见《课程考核方案》。'),
        ('h2', '六、学习建议'),
        ('p', '软件测试是一门实践性很强的课程。建议课前预习对应章节，课上跟随案例动手分析，课后按周完成实验任务，并把课程设计项目贯穿全学期，边学边测、持续改进。'),
    ]


def doc_大纲():
    info = [
        ['课程名称', f"（中文）{C['name']}"],
        ['课程名称', f"（英文）{C['en']}"],
        ['课程编码', C['code'], '课程性质', C['nature']],
        ['课程类型', C['ctype'], '', ''],
        ['适用专业', C['major'], '', ''],
        ['开课部门', C['college'], '课程负责人', C['teacher']],
        ['学分 / 学时', f"总学分：{C['credits']}", '', f"总学时：{C['hours']}"],
        ['学时分配', f"理论：{C['theory']}", '实验：' + str(C['practice']), '实践：0'],
        ['授课语言', C['lang'], '', ''],
        ['先修课程', C['prereq'], '', ''],
        ['周次安排', '第 1–17 周（含实验）', '考核方式', C['exam']],
    ]
    goals = [
        ['课程目标1', '掌握软件测试的基本概念、理论与方法，理解测试在软件工程与质量保证中的地位和作用。'],
        ['课程目标2', '能够针对具体需求开展测试分析，设计测试计划与测试用例，选择合适的方法与工具。'],
        ['课程目标3', '能够搭建测试环境、执行测试、使用自动化工具，规范记录与跟踪缺陷。'],
        ['课程目标4', '能够评估测试结果、撰写测试报告，具备团队协作与工程表达能力。'],
    ]
    mapping = [
        ['毕业要求', '分解指标点', '指标点内容', '课程目标'],
        ['毕业要求3 设计/开发解决方案', '3.2', '能够针对特定需求完成模块或系统的设计，并体现创新意识。', '课程目标2（H）'],
        ['毕业要求4 研究', '4.1', '能够基于科学原理并采用科学方法对复杂工程问题进行研究，包括设计实验、分析与解释数据。', '课程目标3（M）'],
        ['毕业要求5 使用现代工具', '5.1', '能够选择与使用恰当的现代工程工具和信息技术工具，对复杂工程问题进行分析、设计与开发。', '课程目标3（H）'],
        ['毕业要求10 沟通', '10.1', '能够就复杂工程问题与业界同行及社会公众进行有效沟通和交流，包括撰写报告和设计文稿。', '课程目标4（M）'],
        ['毕业要求11 项目管理', '11.2', '理解并掌握工程管理原理与经济决策方法，并能在多学科环境中应用。', '课程目标4（L）'],
    ]
    teach = [['周次', '章节', '主要内容', '学习目标★', '学时', '支撑课程目标']]
    for s in SESSIONS:
        teach.append([str(s['no']), s['chapter'], '；'.join(s['points']), '；'.join(s['goal']), '2', s['support']])
    assess = [
        ['考核方式', '考核要求', '比重', '对应课程目标'],
        ['考勤与课堂表现', '按时出勤，积极参与课堂讨论、练习与互评。', '10%', '1–4'],
        ['实验与阶段任务', '完成各章实验任务，提交测试用例、脚本与实验记录。', '40%', '2–3'],
        ['课程设计作品与答辩', '完成完整测试实践并提交报告、参加答辩，方案合理、证据完整。', '50%', '1–4'],
    ]
    std_assess = [
        ['考核方式', '基本要求', '优秀（90–100）', '良好（80–89）', '中等（70–79）', '及格（60–69）', '不及格（0–59）'],
        ['实验与阶段任务', '按要求完成实验并提交规范成果。', '独立完成、结果正确、记录规范、有深入分析。', '独立完成、结果正确、记录较规范。', '基本完成、结果基本正确。', '在指导下完成，成果有欠缺。', '未完成或结果错误。'],
        ['课程设计作品与答辩', '完成测试方案、执行与报告并答辩。', '方案完整、用例充分、发现关键缺陷、报告规范、答辩清晰。', '方案较完整、用例较充分、报告较规范。', '基本完成各项任务，成果一般。', '在指导下完成，成果存在不足。', '未完成或成果缺失。'],
    ]
    goal_std = [['课程目标', '考核依据', '优秀（90–100）', '良好（80–89）', '中等（70–79）', '及格（60–69）', '不及格（0–59）']]
    for g, name in [('课程目标1', '对测试基本概念、理论与方法的掌握'), ('课程目标2', '测试分析与用例设计的质量'),
                    ('课程目标3', '测试执行、工具使用与缺陷跟踪'), ('课程目标4', '结果评估、报告与答辩表达')]:
        goal_std.append([g, name, '掌握扎实，能灵活运用。', '掌握较好，能正确运用。', '基本掌握，运用基本正确。', '基本掌握，运用有欠缺。', '掌握不足，不能正确运用。'])
    return [
        ('h1', f"《{C['name']}》课程教学大纲"),
        ('p', f"课程名称：{C['name']}　课程代码：{C['code']}　开课单位：{C['college']}"),
        ('p', f"制定人：{C['teacher']}　审核人：{C['reviewer']}　制定时间：{C['date']}　广东金融学院教务处 制"),
        ('h2', '一、课程信息'),
        ('table', [['项目', '内容', '项目', '内容']] + info),
        ('h2', '二、课程简介'),
        ('table', [[
            f"课程的学科背景：软件测试是软件工程的重要组成部分，是保证软件质量、降低质量成本的关键活动。"
            f"开设目的和意义：{C['aim']}"
            f"主要内容：软件测试基本概念与方法、测试流程与规范、单元与集成测试、系统功能测试、专项测试、本地化测试、测试自动化、测试需求分析与计划、用例设计与维护、测试基础设施、测试执行与报告。"
            f"课程特色与思政教育：以质量即责任为主线，结合《血狮》、火星探测器等真实案例，融入质量意识、工程伦理、团队协作与科技报国教育。"
            f"教学与考核方式：多媒体授课＋实验实践，考查（过程性考核＋课程设计作品与答辩）。"
            f"学生通过学习该课程可获得的知识、技能、能力和素养★：能够设计测试用例、使用自动化工具完成完整的项目测试，具备测试分析与工程实践能力。"
        ]]),
        ('h2', '三、课程目标★'),
        ('table', goals),
        ('h2', '四、课程目标与毕业要求的对应关系'),
        ('p', '课程目标对毕业要求的支撑如下（H 高、M 中、L 低）：'),
        ('table', mapping),
        ('h2', '五、教学内容、要求及进度安排★'),
        ('table', teach),
        ('p', '各章/各次课主要内容、学习目标与学习重难点如下：'),
        ('table', [['周次/章节', '主要内容', '学习目标★', '学习重难点★', '学时', '支撑课程目标']] +
                  [[f"第 {s['no']} 次课 {s['chapter']}", '；'.join(s['points']), '；'.join(s['goal']),
                    '重点：' + '；'.join(s['key']) + '；难点：' + '；'.join(s['hard']), '2', s['support']]
                   for s in SESSIONS]),
        ('p', '说明：第 1–16 周为教学与实验周，每周 2 学时，合计 32 学时；第 17 周为考核周，用于课程设计成果评定与成绩提交。'),
        ('h2', '六、考核与评价'),
        ('h3', '（一）考核方式'),
        ('table', assess),
        ('h3', '（二）评价标准'),
        ('p', '1. 考核方式评价标准'),
        ('table', std_assess),
        ('p', '2. 课程目标评价标准'),
        ('table', goal_std),
        ('h2', '七、课程思政元素与融入设计'),
        ('p', '依据教育部《高等学校课程思政建设指导纲要》（教高〔2020〕3 号）对工学类课程"强化工程伦理教育、培养精益求精的大国工匠精神、激发科技报国的家国情怀和使命担当"的要求，本课程以"质量即责任"为主线，设置六个思政维度并与 16 次课一一对应，做到每次课有载体、有活动、有观测点，避免"贴标签""两张皮"。'),
        ('table', [['思政维度', '融入要点', '主要载体']] + [
            ['家国情怀与科技报国', '把软件质量与国家关键信息基础设施安全、自主可控联系起来', 'Therac-25、波音星际客机、国产《血狮》案例；信创适配与国产工具'],
            ['科学精神与批判思维', '以证据说话、敢于证伪，理解测试是获取质量信息的科学活动', '探索式测试会话、假设—验证记录、缺陷模式'],
            ['工程伦理与职业道德', '测试人员的严谨、负责、诚信，不隐瞒缺陷、不放水', '真实事故复盘、"缺陷要不要发版"角色辩论'],
            ['工匠品质与质量意识', '精益求精、规范操作、缺陷描述客观可复现', '用例评审会、缺陷报告演练、测试报告如实披露风险'],
            ['法治意识与安全合规', '遵守网络安全法、数据安全法、个人信息保护法，保护测试数据', '数据脱敏要求、压力测试不危害生产系统的边界讨论'],
            ['团队协作与诚信', '分工协作、对事不对人的评审文化、如实汇报进度与风险', '小组代码互评、课程设计项目与答辩诚信提问'],
        ]),
        ('p', '评价方式：不设独立思政分数，把观测点嵌入既有考核项——考勤与课堂表现 10% 关注价值表达与协作态度；实验与阶段任务 40% 关注数据真实、操作规范、数据脱敏与按时提交；课程设计 50% 关注缺陷描述客观可复现、报告如实披露遗留风险、答辩如实回应。'),
        ('h2', '八、教材、参考文献与其他教学资源'),
        ('table', [['类别', '内容'],
                   ['使用教材', C['textbook']],
                   ['参考书目', '；'.join(C['refs'])],
                   ['教学资源', '课程 HTML 课件（40 页第 1 章、交互练习与案例）、参考资料 10 份（标准规范、行业报告、知识地图等）、实验环境与工具（JUnit、Postman/JMeter、Selenium/Playwright、TestLink、SonarQube 等）。']]),
        ('h2', '九、说明'),
        ('p', '本大纲经课程所属学院制定人、审订人、审批人签字后方可生效，以上内容由课程所属学院负责解释。执行中如遇教学计划调整，以学院与教务处备案为准。'),
        ('table', [['制定人：' + C['teacher'], '审订人：', '审批人：'], ['', '', '时间：' + C['date']]]),
    ]


def doc_进度表():
    rows = [['周次', '课程章节', '教 学 内 容', '授课/学时', '教学方式']] + [list(r) for r in WEEK_TABLE]
    return [
        ('h1', f"{C['name']}  课 程 教 学 进 度 表"),
        ('p', f"课程名称：{C['name']}　课程代码：{C['code']}"),
        ('p', f"开课系（部）：{C['college']}　制定人：{C['teacher']}　审核人：{C['reviewer']}"),
        ('p', f"制定时间：{C['date']}　广东金融学院教务处 制"),
        ('p', f"学时：{C['hours']}　学分：{C['credits']}　周次：第 1–17 周（含实验）"),
        ('table', rows),
        ('p', '说明：第 1–16 周每周 2 学时，共 32 学时；第 17 周为考核周（不计学时），用于课程设计成果评定与成绩提交。实验任务随对应章节同步开展。'),
    ]


def doc_教案():
    b = [
        ('h1', f"《{C['name']}》课程教案"),
        ('p', f"课程名称：{C['name']}　课程代码：{C['code']}"),
        ('p', f"开课学院（部）：{C['college']}　制定人：{C['teacher']}　审核人：{C['reviewer']}"),
        ('p', f"制定时间：{C['date']}　广东金融学院教务处 制"),
        ('h2', '一、课程简介'),
        ('l1', f"课程类别：{C['nature']}"),
        ('l1', f"授课对象：{C['major']}（{C['classes']}）"),
        ('l1', f"学时与学分：{C['hours']} 学时，{C['credits']} 学分（理论 {C['theory']} ＋ 实践 {C['practice']}）"),
        ('l1', '使用教材：' + C['textbook']),
        ('l1', '参考教材：'),
    ] + [('l1', f"{i}. {r}") for i, r in enumerate(C['refs'][:8], 1)] + [
        ('h2', '二、教学目的与教学任务'),
        ('p', '性质：本课程是软件工程与软件测试交叉的实践性课程，面向软件测试岗位能力培养，是计算机类专业的专业课程。'),
        ('p', '目的：' + C['aim']),
        ('p', '任务：完成软件测试基本理论与方法的学习与实验；以一个具体软件系统为对象完成课程设计项目，包括测试需求分析、测试计划、测试用例设计、测试执行、缺陷报告与测试总结，并进行项目答辩。'),
    ]
    for s in SESSIONS:
        b.append(('h2', f"第 {s['no']} 次课　{s['chapter']}　{s['title']}　2 学时"))
        b.append(('p', f"教学方式：{s['method']}　支撑课程目标：{s['support']}"))
        b.append(('p', '本次教学重点：'))
        b += [('l1', x) for x in s['key']]
        b.append(('p', '本次教学难点：'))
        b += [('l1', x) for x in s['hard']]
        b.append(('p', '本次课教学内容：'))
        b += [('l1', x) for x in s['points']]
        b.append(('p', '本次课学习目标：'))
        b += [('l1', x) for x in s['goal']]
        b += [('p', x) for x in s['detail']]
        outline = chapter_outline(s)
        if outline:
            b += outline
        else:
            b += [
                ('l1', '项目成果验收要点：测试对象与范围、测试策略与依据、用例设计与覆盖、执行记录与缺陷清单、测试结论与发布建议。'),
                ('l1', '汇报结构建议：项目背景与测试目标 → 测试方案与用例设计 → 执行过程与发现 → 缺陷分析与质量评估 → 经验总结与改进。'),
                ('l1', '答辩评分要点：方案合理性、用例质量、执行严谨性、报告规范性、表达与回应能力。'),
                ('l1', '常见问题准备：为什么选择这些测试方法？覆盖率如何？发现的缺陷如何定级？如果时间更充分会补充什么测试？'),
            ]
        b.append(('p', '案例讨论：' + s['case']))
        b.append(('p', '教学组织：'))
        b += [('l1', x) for x in s['org']]
        b.append(('p', '作业布置：'))
        b += [('l1', x) for x in s['hw']]
        b.append(('p', '本次课推荐参考文献：'))
        b += [('l1', x) for x in C['refs'][:5]]
        sz = SZ_DESIGN.get(s['no'])
        if sz:
            dim, carrier, how, ev = sz
            b.append(('p', '本次课程思政教学设计：'))
            b.append(('l1', f"思政维度：{dim}"))
            b.append(('l1', f"融入载体：{carrier}"))
            b.append(('l1', f"课堂实施：{how}"))
            b.append(('l1', f"评价观测点：{ev}"))
        else:
            b.append(('p', '本次课程思政教学设计：' + s['sz']))
        b.append(('p', '本次课实践教学设计：'))
        b += [('l1', x) for x in s['practice']]
        b.append(('p', '课后自我总结分析：根据课堂提问、实验完成情况与作业反馈，记录学生易错点与薄弱环节，作为下次课复习、例题选择与个别辅导的依据。'))
    return b


def doc_考核():
    return [
        ('h1', '广东金融学院课程考核方案'),
        ('p', f"教学单位：{C['college']}　　{C['date']}"),
        ('table', [
            ['课程名称', C['name'], '', '任课教师', C['teacher']],
            ['考试班级', C['classes'], '', '课程性质', C['nature']],
            ['期末考核方式', '统考（　）　随堂考试（　）　考查（ √ ）', '', '', ''],
            ['总评成绩构成', '项目', '评分标准', '', ''],
            ['过程性考核（50%）', '考勤与课堂表现（10%）', '100 分满分：出勤 50 分、课堂讨论与练习 50 分，按次记录、期末折算。', '', ''],
            ['过程性考核（50%）', '实验与阶段任务（40%）', '100 分满分：按各章实验任务的完成质量、规范性与提交及时性计分，多次取加权平均。', '', ''],
            ['课程设计考核（50%）', '测试计划与用例设计（15%）', '100 分满分：测试范围清晰、策略合理、用例覆盖充分、步骤与预期结果规范。', '', ''],
            ['课程设计考核（50%）', '测试执行与缺陷报告（15%）', '100 分满分：执行记录完整、缺陷可复现、缺陷描述规范、跟踪闭环。', '', ''],
            ['课程设计考核（50%）', '测试报告与答辩（20%）', '100 分满分：报告结构完整、数据准确、结论有据；答辩表达清楚、回答准确。', '', ''],
            ['备注', '本课程为考查类课程，以过程性考核与课程设计成果为主，不组织闭卷笔试；成绩构成比例可由任课教师根据教学实际微调，但需在开课时向学生公布。', '', '', ''],
        ]),
        ('p', '注：以上成绩构成项目及比例请根据课程实际情况填写。'),
        ('p', '任课教师签字：'),
    ]


def doc_教材():
    return [
        ('h1', f"《{C['name']}》“教材及参考书目录”"),
        ('table', [
            ['使用教材', C['textbook']],
            ['参考书目', '；'.join(C['refs'])],
        ]),
    ]


def doc_教师():
    return [
        ('h1', '教师简介'),
        ('table', [
            ['姓名', '性别', '出生年月', '职称/职务', '硕士毕业院校', '博士毕业院校', '参加工作时间'],
            [C['teacher'], '男', '1988-07', '讲师', '中山大学', '华南师范大学', '2011.08'],
        ]),
        ('table', [
            ['承担过的课程', C['courses_taught']],
            ['目前承担的课程', C['courses_now']],
        ]),
        ('h2', '教学与研究方向'),
        ('p', '主要承担软件工程、软件测试、软件项目管理等课程的教学工作，研究方向为软件质量与软件测试、软件工程教育。'),
        ('h2', '联系方式'),
        ('p', f"电话/微信：{C['phone']}"),
    ]


def doc_说明():
    return [
        ('h1', f"《{C['name']}》教学基础文件说明"),
        ('p', f"本目录仿照《软件项目管理教学基础文件（周宇文26版）》中除 PPT 外的全部文件格式制作，课程信息取自《课程.xls》与教师确认口径：{C['name']}（课程编码 {C['code']}），{C['nature']}，{C['hours']} 学时 / {C['credits']} 学分，第 1–17 周（含实验），责任人 {C['teacher']}。"),
        ('h2', '一、文件清单（docx + 可复制 md 各一份）'),
        ('l1', '01-课程简介：课程名称/代码/性质/学时学分/开设目的/修读条件/主要内容/教材/参考书/考核方式（源包 9 项齐全，并补充开课学院、适用专业、授课对象、任课教师）。'),
        ('l1', '02-课程教学大纲：一~八节齐全，含课程信息表、课程简介长文、课程目标 4 条、目标与毕业要求对应（H/M/L）、16 次课进度表与各次课主要内容/学习目标/学习重难点表、考核方式与两套评价标准、教材文献与教学资源、说明与签字表。'),
        ('l1', '03-教学进度表：学院样式表头（含广东金融学院教务处 制）＋ 17 周进度表（第 1–16 周教学 2 学时、第 17 周考核）。'),
        ('l1', '04-课程教案：16 次课完整教案，每次课含教学重点、难点、教学内容、学习目标、**教学内容知识点大纲（按知识节编号列出，每节含要点条目）**、案例讨论、教学组织、作业布置、参考文献、**课程思政设计（思政维度/融入载体/课堂实施/评价观测点）**、实践教学设计、课后总结。'),
        ('l1', '05-课程考核方案：学院样式表格，期末考核方式为考查，含过程性考核（考勤 10%＋实验 40%）与课程设计考核（计划与用例 15%＋执行与缺陷 15%＋报告与答辩 20%）逐项评分标准与备注。'),
        ('l1', '06-教材及参考书目录：使用教材 1 部＋参考书目 25 条（专著、标准、报告、工具文档）。'),
        ('l1', '07-教师简介：基本信息表＋承担过的课程＋目前承担的课程＋研究方向与联系方式。'),
        ('l1', '另附《课程思政融入设计-文献调研与实施方案》：教育部《高等学校课程思政建设指导纲要》要点、公开文献检索结论、五条可复用融入机制与 16 次课思政元素地图。'),
        ('h2', '二、两版用途'),
        ('l1', 'docx 版：可直接打印、上交或继续编辑（正文宋体小四，表格加框线）。'),
        ('l1', 'md 版：可复制版，同一段落内不换行，复制到 Word/网页/聊天工具不会带回车，便于二次排版。'),
        ('h2', '三、需要教师确认的信息'),
        ('l1', '审核人沿用周瑞红；制定时间暂填 2026-09-13。'),
        ('l1', '考试班级暂写计算机学院相关专业本科生（以教务选课名单为准）。'),
        ('l1', '课程性质按教师确认填专业选修；课程编码沿用 154442008（xls 登记为拓展教育选修课，如学校另有专业选修编码请替换）。'),
        ('l1', '考核比例按考勤 10%＋实验 40%＋课程设计 50% 填写，可在考核方案中微调。'),
        ('l1', '毕业要求指标点编号参照学校通用模板填写，如学院有统一指标点表请替换。'),
    ]


def main():
    save('01-课程简介', doc_简介())
    save('02-课程教学大纲', doc_大纲())
    save('03-教学进度表', doc_进度表())
    save('04-课程教案', doc_教案())
    save('05-课程考核方案', doc_考核())
    save('06-教材及参考书目录', doc_教材())
    save('07-教师简介', doc_教师())
    save('00-说明', doc_说明())


if __name__ == '__main__':
    main()
