feat: 为metadata、模板和幻灯片添加description字段支持
添加可选的description字段用于文档目的,不影响渲染输出。 主要更改: - core/presentation.py: 添加metadata.description属性 - core/template.py: 添加template.description属性 - tests: 添加16个新测试用例验证description功能 - docs: 更新README.md和README_DEV.md文档 - specs: 新增page-description规范文件
This commit is contained in:
48
README.md
48
README.md
@@ -136,6 +136,16 @@ metadata:
|
||||
dpi: 150 # 高质量输出
|
||||
```
|
||||
|
||||
#### description 字段
|
||||
|
||||
`metadata.description` 字段用于描述整个演示文稿的概要和用途,仅用于文档目的,不影响生成的 PPTX 文件:
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
size: "16:9"
|
||||
description: "2024年度项目进展总结,包含背景、成果和展望"
|
||||
```
|
||||
|
||||
### 使用模板
|
||||
|
||||
```yaml
|
||||
@@ -418,6 +428,27 @@ slides:
|
||||
author: "作者"
|
||||
```
|
||||
|
||||
#### 模板 description 字段
|
||||
|
||||
模板文件可以包含可选的 `description` 字段,用于描述模板的用途和设计意图,仅用于文档目的:
|
||||
|
||||
```yaml
|
||||
# templates/title-slide.yaml
|
||||
description: "用于章节标题页的模板,包含主标题和副标题"
|
||||
|
||||
vars:
|
||||
- name: title
|
||||
required: true
|
||||
|
||||
elements:
|
||||
- type: text
|
||||
box: [1, 2, 8, 1]
|
||||
content: "{title}"
|
||||
font:
|
||||
size: 44
|
||||
bold: true
|
||||
```
|
||||
|
||||
### 混合模式模板
|
||||
|
||||
混合模式允许你在使用模板的同时添加自定义元素,实现更灵活的布局组合。
|
||||
@@ -583,6 +614,23 @@ slides:
|
||||
content: "额外内容"
|
||||
```
|
||||
|
||||
#### 幻灯片 description 字段
|
||||
|
||||
幻灯片可以包含可选的 `description` 字段,用于描述该幻灯片的作用和内容,仅用于文档目的,不影响生成的 PPTX 文件:
|
||||
|
||||
```yaml
|
||||
slides:
|
||||
- description: "介绍项目背景和目标"
|
||||
template: title-slide
|
||||
vars:
|
||||
title: "项目背景"
|
||||
|
||||
- description: "展示核心功能特性"
|
||||
elements:
|
||||
- type: text
|
||||
content: "功能特性"
|
||||
```
|
||||
|
||||
### 条件渲染
|
||||
|
||||
#### 元素级条件渲染
|
||||
|
||||
@@ -595,6 +595,48 @@ if right > slide_width + TOLERANCE:
|
||||
- 检查变量定义是否有必需的 `name` 字段
|
||||
- 检测默认值中引用不存在的变量
|
||||
|
||||
### 8. description 字段
|
||||
|
||||
**决策**:为 metadata、模板和幻灯片添加可选的 `description` 字段
|
||||
|
||||
**理由**:
|
||||
- 自文档化:提高模板和演示文稿的可读性和可维护性
|
||||
- 不影响渲染:description 仅用于文档目的,不参与 PPTX 生成
|
||||
- 完全向后兼容:字段为可选,现有文件无需修改
|
||||
|
||||
**实现要点**:
|
||||
|
||||
1. **数据模型**:
|
||||
- `Presentation.description`:从 `metadata.description` 读取
|
||||
- `Template.description`:从模板文件的 `description` 字段读取
|
||||
- `Slide.description`:在 `render_slide()` 返回值中保留
|
||||
|
||||
2. **YAML 解析**:
|
||||
- 使用 `.get('description')` 自动处理缺失情况(返回 None)
|
||||
- YAML 原生支持多行文本格式
|
||||
|
||||
3. **不参与渲染**:
|
||||
- description 字段不传递给渲染器
|
||||
- 不写入最终的 PPTX 文件
|
||||
|
||||
**示例**:
|
||||
```yaml
|
||||
# metadata description
|
||||
metadata:
|
||||
description: "2024年度项目进展总结"
|
||||
|
||||
# 模板 description
|
||||
templates:
|
||||
title-slide:
|
||||
description: "用于章节标题页的模板"
|
||||
elements: [...]
|
||||
|
||||
# 幻灯片 description
|
||||
slides:
|
||||
- description: "介绍项目背景"
|
||||
template: title-slide
|
||||
```
|
||||
|
||||
## 扩展指南
|
||||
|
||||
### 添加新元素类型
|
||||
|
||||
@@ -32,6 +32,7 @@ class Presentation:
|
||||
metadata = self.data.get("metadata", {})
|
||||
self.size = metadata.get("size", "16:9")
|
||||
self.dpi = metadata.get("dpi", 96)
|
||||
self.description = metadata.get("description") # 可选的描述字段
|
||||
|
||||
# 验证尺寸值
|
||||
if not isinstance(self.size, str):
|
||||
@@ -134,4 +135,5 @@ class Presentation:
|
||||
return {
|
||||
"background": slide_data.get("background"),
|
||||
"elements": element_objects,
|
||||
"description": slide_data.get("description"), # 保留幻灯片描述
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ class Template:
|
||||
self.data = load_yaml_file(template_path)
|
||||
validate_template_yaml(self.data, str(template_path))
|
||||
|
||||
# 可选的描述字段
|
||||
self.description = self.data.get('description')
|
||||
|
||||
# 解析变量定义
|
||||
self.vars_def = {}
|
||||
for var in self.data.get('vars', []):
|
||||
@@ -79,6 +82,9 @@ class Template:
|
||||
# 初始化条件评估器
|
||||
obj._condition_evaluator = ConditionEvaluator()
|
||||
|
||||
# 可选的描述字段
|
||||
obj.description = template_data.get('description')
|
||||
|
||||
# 解析变量定义
|
||||
obj.vars_def = {}
|
||||
for var in template_data.get('vars', []):
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-03-04
|
||||
@@ -0,0 +1,82 @@
|
||||
# Design: Add Description Field to Metadata, Templates, and Slides
|
||||
|
||||
## Context
|
||||
|
||||
当前项目使用YAML格式的模板文件和演示文稿定义。YAML文件包含metadata(元数据)部分和slides列表。模板系统(`template-system` spec)定义了模板的结构,包括vars、elements等字段。演示文稿通过YAML文件定义slides列表,每个slide可以引用模板或定义自定义元素。
|
||||
|
||||
项目目前没有内置的机制来描述文档整体、页面或幻灯片的用途。开发者只能通过元素内容和变量名来推断,这降低了演示文稿、模板和幻灯片的可维护性。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 为metadata添加可选的description字段
|
||||
- 为模板文件添加可选的description字段
|
||||
- 为幻灯片定义添加可选的description字段
|
||||
- 确保description字段在数据模型中被正确解析和保留
|
||||
- 保持完全的向后兼容性
|
||||
- 不影响渲染逻辑和输出结果
|
||||
|
||||
**Non-Goals:**
|
||||
- 不将description写入最终的PPTX文件(PowerPoint备注功能不在本次范围)
|
||||
- 不实现description的验证逻辑(如长度限制、格式要求)
|
||||
- 不实现基于description的搜索或过滤功能
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Description字段为可选的字符串类型
|
||||
|
||||
**决策:** description字段为可选字段,类型为字符串,可以为空字符串。
|
||||
|
||||
**理由:**
|
||||
- 保持简单,不过度设计
|
||||
- 用户可以自由描述,不受格式限制
|
||||
- 可选设计确保向后兼容
|
||||
|
||||
**替代方案考虑:**
|
||||
- 使用结构化格式(如对象,包含title、content等子字段)- 过于复杂,当前需求不需要
|
||||
|
||||
### 2. Description不参与渲染
|
||||
|
||||
**决策:** description字段仅用于文档目的,不传递给渲染器,不影响PPTX生成。
|
||||
|
||||
**理由:**
|
||||
- PowerPoint的备注功能需要额外处理,且不在当前需求范围
|
||||
- 保持渲染逻辑简单,避免引入不必要的复杂性
|
||||
|
||||
**替代方案考虑:**
|
||||
- 将description写入PPTX备注 - 需要额外的python-pptx API调用,增加复杂度
|
||||
|
||||
### 3. 在数据模型层面实现
|
||||
|
||||
**决策:** 在数据模型类(Metadata/Document、Template、Slide)中添加description属性,在YAML解析时读取该字段。
|
||||
|
||||
**理由:**
|
||||
- 与现有代码结构一致
|
||||
- 便于统一处理和访问
|
||||
- metadata中的description可以描述整个文档
|
||||
|
||||
**替代方案考虑:**
|
||||
- 仅在YAML解析时保留,不添加到数据模型 - 会导致信息丢失,不利于后续使用
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| 风险 | 缓解措施 |
|
||||
|------|----------|
|
||||
| description字段可能被滥用或包含不恰当内容 | 依赖用户自律,不添加内容验证 |
|
||||
| 增加数据模型的字段数量 | 影响很小,仅增加一个可选字符串字段 |
|
||||
| 用户可能期望description能影响输出 | 在文档中明确说明description仅用于文档目的 |
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 更新数据模型类,添加description属性(Metadata、Template、Slide)
|
||||
2. 更新YAML解析器,读取metadata、模板和幻灯片的description字段
|
||||
3. 添加测试用例验证description正确读取和保留
|
||||
4. 更新文档说明新字段的用法
|
||||
|
||||
**回滚策略:**
|
||||
- 由于是新增可选字段,移除该字段不会影响现有功能
|
||||
- 如需回滚,只需从数据模型和解析器中移除description相关代码
|
||||
|
||||
## Open Questions
|
||||
|
||||
无。本设计较为简单直接,没有未解决的技术问题。
|
||||
@@ -0,0 +1,36 @@
|
||||
# Proposal: Add Description Field to Pages and Slides
|
||||
|
||||
## Why
|
||||
|
||||
当前项目和模板系统中缺乏对文档、页面和幻灯片用途的内置说明机制。当用户或开发者查看演示文稿YAML时,难以快速理解整个文档的概要、某个模板的设计意图或幻灯片的使用场景。添加description字段可以提供自文档化的能力,提高演示文稿、模板和幻灯片的可维护性和可读性。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 为metadata添加可选的 `description` 字段,用于描述整个演示文稿的概要和用途
|
||||
- 为模板文件添加可选的 `description` 字段,用于说明该模板的用途和设计意图
|
||||
- 为幻灯片定义添加可选的 `description` 字段,用于说明该幻灯片的作用和内容
|
||||
- description字段为纯文本字符串,完全可选,不影响现有渲染逻辑
|
||||
- 保留description字段以供工具和文档生成使用
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `page-description`: 为模板页面和幻灯片添加描述字段的功能
|
||||
|
||||
### Modified Capabilities
|
||||
无现有能力的需求变更,仅新增可选字段
|
||||
|
||||
## Impact
|
||||
|
||||
**受影响的代码模块:**
|
||||
- `models/` - 元数据、模板和幻灯片数据模型需要支持description字段
|
||||
- `yaml_parsing/` - YAML解析器需要读取metadata和模板、幻灯片的description字段
|
||||
- `specs/` - 新增page-description规范文档
|
||||
|
||||
**不受影响的模块:**
|
||||
- 渲染逻辑 - description字段不影响视觉渲染
|
||||
- 输出PPTX - description不写入最终的PPTX文件
|
||||
|
||||
**向后兼容性:**
|
||||
- 完全向后兼容,description为可选字段
|
||||
- 现有模板和YAML文件无需修改即可继续使用
|
||||
@@ -0,0 +1,130 @@
|
||||
# Page Description
|
||||
|
||||
## Purpose
|
||||
|
||||
Page Description功能允许用户为文档元数据、模板和幻灯片添加描述信息,用于说明文档概要、页面的用途、设计意图或内容概要。这提高了演示文稿、模板和幻灯片的可读性和可维护性。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: metadata必须支持可选的description字段
|
||||
|
||||
演示文稿的metadata SHALL 支持可选的 `description` 字段,用于描述整个演示文稿的概要和用途。
|
||||
|
||||
#### Scenario: metadata包含description字段
|
||||
|
||||
- **WHEN** YAML文件的metadata定义了 `description: "这是关于项目年度总结的演示文稿"`
|
||||
- **THEN** 系统成功加载该描述字段,可通过metadata对象访问
|
||||
|
||||
#### Scenario: metadata不包含description字段
|
||||
|
||||
- **WHEN** YAML文件的metadata未定义 `description` 字段
|
||||
- **THEN** 系统正常加载演示文稿,description属性为None或空字符串
|
||||
|
||||
#### Scenario: metadata description为空字符串
|
||||
|
||||
- **WHEN** metadata定义了 `description: ""`
|
||||
- **THEN** 系统接受空字符串作为有效值
|
||||
|
||||
### Requirement: 模板必须支持可选的description字段
|
||||
|
||||
模板 SHALL 支持可选的 `description` 字段,用于描述该模板的用途和设计意图。
|
||||
|
||||
#### Scenario: 模板包含description字段
|
||||
|
||||
- **WHEN** 模板文件定义了 `description: "用于章节标题页的模板,包含主标题和副标题"`
|
||||
- **THEN** 系统成功加载该描述字段,可通过模板对象访问
|
||||
|
||||
#### Scenario: 模板不包含description字段
|
||||
|
||||
- **WHEN** 模板文件未定义 `description` 字段
|
||||
- **THEN** 系统正常加载模板,description属性为None或空字符串
|
||||
|
||||
#### Scenario: description为空字符串
|
||||
|
||||
- **WHEN** 模板文件定义了 `description: ""`
|
||||
- **THEN** 系统接受空字符串作为有效值
|
||||
|
||||
### Requirement: 幻灯片必须支持可选的description字段
|
||||
|
||||
幻灯片定义 SHALL 支持可选的 `description` 字段,用于描述该幻灯片的作用和内容。
|
||||
|
||||
#### Scenario: 幻灯片包含description字段
|
||||
|
||||
- **WHEN** 幻灯片定义了 `description: "介绍项目背景和目标"`
|
||||
- **THEN** 系统成功加载该描述字段,可通过幻灯片对象访问
|
||||
|
||||
#### Scenario: 幻灯片不包含description字段
|
||||
|
||||
- **WHEN** 幻灯片定义未包含 `description` 字段
|
||||
- **THEN** 系统正常处理幻灯片,description属性为None或空字符串
|
||||
|
||||
### Requirement: description字段不得影响渲染逻辑
|
||||
|
||||
系统 SHALL 在渲染过程中忽略 `description` 字段,不影响最终的PPTX输出。
|
||||
|
||||
#### Scenario: 渲染包含description的模板
|
||||
|
||||
- **WHEN** 系统渲染包含 `description` 字段的模板
|
||||
- **THEN** description不参与元素渲染,不影响输出结果
|
||||
|
||||
#### Scenario: 渲染包含description的幻灯片
|
||||
|
||||
- **WHEN** 系统渲染包含 `description` 字段的幻灯片
|
||||
- **THEN** description不写入PPTX文件,不影响输出结果
|
||||
|
||||
### Requirement: YAML解析器必须正确解析description字段
|
||||
|
||||
系统 SHALL 从YAML文件中正确读取 `description` 字段,并将其传递给数据模型。
|
||||
|
||||
#### Scenario: 解析metadata中的description
|
||||
|
||||
- **WHEN** YAML文件的metadata包含 `description: "文档描述"`
|
||||
- **THEN** 解析器将该字符串传递给metadata对象的description属性
|
||||
|
||||
#### Scenario: 解析模板中的description
|
||||
|
||||
- **WHEN** YAML文件中的模板包含 `description: "这是描述"`
|
||||
- **THEN** 解析器将该字符串传递给模板对象的description属性
|
||||
|
||||
#### Scenario: 解析幻灯片中的description
|
||||
|
||||
- **WHEN** YAML文件中的幻灯片包含 `description: "幻灯片描述"`
|
||||
- **THEN** 解析器将该字符串传递给幻灯片对象的description属性
|
||||
|
||||
### Requirement: 数据模型必须包含description属性
|
||||
|
||||
元数据、模板和幻灯片数据模型 SHALL 包含 `description` 属性,用于存储描述信息。
|
||||
|
||||
#### Scenario: metadata对象包含description属性
|
||||
|
||||
- **WHEN** 开发者访问metadata对象
|
||||
- **THEN** 可以通过 `metadata.description` 获取描述信息
|
||||
|
||||
#### Scenario: 模板对象包含description属性
|
||||
|
||||
- **WHEN** 开发者访问模板对象
|
||||
- **THEN** 可以通过 `template.description` 获取描述信息
|
||||
|
||||
#### Scenario: 幻灯片对象包含description属性
|
||||
|
||||
- **WHEN** 开发者访问幻灯片对象
|
||||
- **THEN** 可以通过 `slide.description` 获取描述信息
|
||||
|
||||
### Requirement: description字段必须支持中文字符
|
||||
|
||||
系统 SHALL 支持在 `description` 字段中使用中文字符和其他Unicode字符。
|
||||
|
||||
#### Scenario: metadata description包含中文
|
||||
|
||||
- **WHEN** metadata的description包含中文字符,如 "这是关于项目的演示文稿"
|
||||
- **THEN** 系统正确处理,不出现编码错误
|
||||
|
||||
#### Scenario: description包含中文
|
||||
|
||||
- **WHEN** 模板或幻灯片的description包含中文字符,如 "这是中文描述"
|
||||
- **THEN** 系统正确处理,不出现编码错误
|
||||
|
||||
#### Scenario: description包含多行文本
|
||||
|
||||
- **WHEN** description字段使用YAML多行文本格式
|
||||
- **THEN** 系统正确读取完整的描述内容
|
||||
@@ -0,0 +1,46 @@
|
||||
# Tasks: Add Description Field to Metadata, Templates, and Slides
|
||||
|
||||
## 1. 数据模型更新
|
||||
|
||||
- [x] 1.1 在Metadata/Document类中添加description属性(可选,默认为None)
|
||||
- [x] 1.2 在Template类中添加description属性(可选,默认为None)
|
||||
- [x] 1.3 在Slide类中添加description属性(可选,默认为None)
|
||||
- [x] 1.4 更新Metadata/Document类的__init__方法接受description参数
|
||||
- [x] 1.5 更新Template类的__init__方法接受description参数
|
||||
- [x] 1.6 更新Slide类的__init__方法接受description参数
|
||||
|
||||
## 2. YAML解析器更新
|
||||
|
||||
- [x] 2.1 更新metadata解析逻辑,读取YAML中的description字段
|
||||
- [x] 2.2 更新模板解析逻辑,读取模板YAML中的description字段
|
||||
- [x] 2.3 更新幻灯片解析逻辑,读取slides列表中的description字段
|
||||
- [x] 2.4 确保description字段缺失时使用默认值None
|
||||
- [x] 2.5 支持多行YAML格式的description文本
|
||||
|
||||
## 3. 测试用例
|
||||
|
||||
- [x] 3.1 添加测试:metadata包含description字段时正确加载
|
||||
- [x] 3.2 添加测试:metadata不包含description字段时正常工作
|
||||
- [x] 3.3 添加测试:模板包含description字段时正确加载
|
||||
- [x] 3.4 添加测试:模板不包含description字段时正常工作
|
||||
- [x] 3.5 添加测试:幻灯片包含description字段时正确加载
|
||||
- [x] 3.6 添加测试:幻灯片不包含description字段时正常工作
|
||||
- [x] 3.7 添加测试:description包含中文字符时正确处理
|
||||
- [x] 3.8 添加测试:description为空字符串时正常工作
|
||||
- [x] 3.9 添加测试:description不影响渲染输出
|
||||
|
||||
## 4. 文档更新
|
||||
|
||||
- [x] 4.1 更新README.md,说明metadata支持description字段
|
||||
- [x] 4.2 更新README.md,说明模板支持description字段
|
||||
- [x] 4.3 更新README.md,说明幻灯片支持description字段
|
||||
- [x] 4.4 在README.md中添加description字段的使用示例
|
||||
- [x] 4.5 更新README_DEV.md,记录description字段的设计说明
|
||||
- [x] 4.6 确保文档中说明description仅用于文档目的,不影响输出
|
||||
|
||||
## 5. 验证和集成
|
||||
|
||||
- [x] 5.1 运行所有现有测试,确保向后兼容性
|
||||
- [x] 5.2 运行新添加的测试用例,确保全部通过
|
||||
- [x] 5.3 使用包含description的示例YAML进行端到端测试
|
||||
- [x] 5.4 验证生成的PPTX文件不受description字段影响
|
||||
130
openspec/specs/page-description/spec.md
Normal file
130
openspec/specs/page-description/spec.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# Page Description
|
||||
|
||||
## Purpose
|
||||
|
||||
Page Description功能允许用户为文档元数据、模板和幻灯片添加描述信息,用于说明文档概要、页面的用途、设计意图或内容概要。这提高了演示文稿、模板和幻灯片的可读性和可维护性。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: metadata必须支持可选的description字段
|
||||
|
||||
演示文稿的metadata SHALL 支持可选的 `description` 字段,用于描述整个演示文稿的概要和用途。
|
||||
|
||||
#### Scenario: metadata包含description字段
|
||||
|
||||
- **WHEN** YAML文件的metadata定义了 `description: "这是关于项目年度总结的演示文稿"`
|
||||
- **THEN** 系统成功加载该描述字段,可通过metadata对象访问
|
||||
|
||||
#### Scenario: metadata不包含description字段
|
||||
|
||||
- **WHEN** YAML文件的metadata未定义 `description` 字段
|
||||
- **THEN** 系统正常加载演示文稿,description属性为None或空字符串
|
||||
|
||||
#### Scenario: metadata description为空字符串
|
||||
|
||||
- **WHEN** metadata定义了 `description: ""`
|
||||
- **THEN** 系统接受空字符串作为有效值
|
||||
|
||||
### Requirement: 模板必须支持可选的description字段
|
||||
|
||||
模板 SHALL 支持可选的 `description` 字段,用于描述该模板的用途和设计意图。
|
||||
|
||||
#### Scenario: 模板包含description字段
|
||||
|
||||
- **WHEN** 模板文件定义了 `description: "用于章节标题页的模板,包含主标题和副标题"`
|
||||
- **THEN** 系统成功加载该描述字段,可通过模板对象访问
|
||||
|
||||
#### Scenario: 模板不包含description字段
|
||||
|
||||
- **WHEN** 模板文件未定义 `description` 字段
|
||||
- **THEN** 系统正常加载模板,description属性为None或空字符串
|
||||
|
||||
#### Scenario: description为空字符串
|
||||
|
||||
- **WHEN** 模板文件定义了 `description: ""`
|
||||
- **THEN** 系统接受空字符串作为有效值
|
||||
|
||||
### Requirement: 幻灯片必须支持可选的description字段
|
||||
|
||||
幻灯片定义 SHALL 支持可选的 `description` 字段,用于描述该幻灯片的作用和内容。
|
||||
|
||||
#### Scenario: 幻灯片包含description字段
|
||||
|
||||
- **WHEN** 幻灯片定义了 `description: "介绍项目背景和目标"`
|
||||
- **THEN** 系统成功加载该描述字段,可通过幻灯片对象访问
|
||||
|
||||
#### Scenario: 幻灯片不包含description字段
|
||||
|
||||
- **WHEN** 幻灯片定义未包含 `description` 字段
|
||||
- **THEN** 系统正常处理幻灯片,description属性为None或空字符串
|
||||
|
||||
### Requirement: description字段不得影响渲染逻辑
|
||||
|
||||
系统 SHALL 在渲染过程中忽略 `description` 字段,不影响最终的PPTX输出。
|
||||
|
||||
#### Scenario: 渲染包含description的模板
|
||||
|
||||
- **WHEN** 系统渲染包含 `description` 字段的模板
|
||||
- **THEN** description不参与元素渲染,不影响输出结果
|
||||
|
||||
#### Scenario: 渲染包含description的幻灯片
|
||||
|
||||
- **WHEN** 系统渲染包含 `description` 字段的幻灯片
|
||||
- **THEN** description不写入PPTX文件,不影响输出结果
|
||||
|
||||
### Requirement: YAML解析器必须正确解析description字段
|
||||
|
||||
系统 SHALL 从YAML文件中正确读取 `description` 字段,并将其传递给数据模型。
|
||||
|
||||
#### Scenario: 解析metadata中的description
|
||||
|
||||
- **WHEN** YAML文件的metadata包含 `description: "文档描述"`
|
||||
- **THEN** 解析器将该字符串传递给metadata对象的description属性
|
||||
|
||||
#### Scenario: 解析模板中的description
|
||||
|
||||
- **WHEN** YAML文件中的模板包含 `description: "这是描述"`
|
||||
- **THEN** 解析器将该字符串传递给模板对象的description属性
|
||||
|
||||
#### Scenario: 解析幻灯片中的description
|
||||
|
||||
- **WHEN** YAML文件中的幻灯片包含 `description: "幻灯片描述"`
|
||||
- **THEN** 解析器将该字符串传递给幻灯片对象的description属性
|
||||
|
||||
### Requirement: 数据模型必须包含description属性
|
||||
|
||||
元数据、模板和幻灯片数据模型 SHALL 包含 `description` 属性,用于存储描述信息。
|
||||
|
||||
#### Scenario: metadata对象包含description属性
|
||||
|
||||
- **WHEN** 开发者访问metadata对象
|
||||
- **THEN** 可以通过 `metadata.description` 获取描述信息
|
||||
|
||||
#### Scenario: 模板对象包含description属性
|
||||
|
||||
- **WHEN** 开发者访问模板对象
|
||||
- **THEN** 可以通过 `template.description` 获取描述信息
|
||||
|
||||
#### Scenario: 幻灯片对象包含description属性
|
||||
|
||||
- **WHEN** 开发者访问幻灯片对象
|
||||
- **THEN** 可以通过 `slide.description` 获取描述信息
|
||||
|
||||
### Requirement: description字段必须支持中文字符
|
||||
|
||||
系统 SHALL 支持在 `description` 字段中使用中文字符和其他Unicode字符。
|
||||
|
||||
#### Scenario: metadata description包含中文
|
||||
|
||||
- **WHEN** metadata的description包含中文字符,如 "这是关于项目的演示文稿"
|
||||
- **THEN** 系统正确处理,不出现编码错误
|
||||
|
||||
#### Scenario: description包含中文
|
||||
|
||||
- **WHEN** 模板或幻灯片的description包含中文字符,如 "这是中文描述"
|
||||
- **THEN** 系统正确处理,不出现编码错误
|
||||
|
||||
#### Scenario: description包含多行文本
|
||||
|
||||
- **WHEN** description字段使用YAML多行文本格式
|
||||
- **THEN** 系统正确读取完整的描述内容
|
||||
@@ -600,3 +600,161 @@ slides:
|
||||
assert result["elements"][1].content == "Template2"
|
||||
assert result["elements"][2].content == "Custom1"
|
||||
assert result["elements"][3].content == "Custom2"
|
||||
|
||||
|
||||
# ============= Description 字段测试 =============
|
||||
|
||||
|
||||
class TestPresentationDescription:
|
||||
"""Presentation description 字段测试类"""
|
||||
|
||||
def test_metadata_with_description(self, temp_dir):
|
||||
"""测试 metadata 包含 description 字段时正确加载"""
|
||||
yaml_content = """
|
||||
metadata:
|
||||
title: "测试演示文稿"
|
||||
description: "这是关于项目年度总结的演示文稿"
|
||||
size: "16:9"
|
||||
slides:
|
||||
- elements: []
|
||||
"""
|
||||
yaml_path = temp_dir / "test.yaml"
|
||||
yaml_path.write_text(yaml_content)
|
||||
|
||||
pres = Presentation(str(yaml_path))
|
||||
|
||||
assert pres.description == "这是关于项目年度总结的演示文稿"
|
||||
|
||||
def test_metadata_without_description(self, temp_dir):
|
||||
"""测试 metadata 不包含 description 字段时正常工作"""
|
||||
yaml_content = """
|
||||
metadata:
|
||||
title: "测试演示文稿"
|
||||
size: "16:9"
|
||||
slides:
|
||||
- elements: []
|
||||
"""
|
||||
yaml_path = temp_dir / "test.yaml"
|
||||
yaml_path.write_text(yaml_content)
|
||||
|
||||
pres = Presentation(str(yaml_path))
|
||||
|
||||
assert pres.description is None
|
||||
|
||||
def test_metadata_description_empty_string(self, temp_dir):
|
||||
"""测试 metadata description 为空字符串时正常工作"""
|
||||
yaml_content = """
|
||||
metadata:
|
||||
title: "测试演示文稿"
|
||||
description: ""
|
||||
size: "16:9"
|
||||
slides:
|
||||
- elements: []
|
||||
"""
|
||||
yaml_path = temp_dir / "test.yaml"
|
||||
yaml_path.write_text(yaml_content)
|
||||
|
||||
pres = Presentation(str(yaml_path))
|
||||
|
||||
assert pres.description == ""
|
||||
|
||||
def test_metadata_description_chinese_characters(self, temp_dir):
|
||||
"""测试 metadata description 包含中文字符时正确处理"""
|
||||
yaml_content = """
|
||||
metadata:
|
||||
title: "测试"
|
||||
description: "这是关于项目的演示文稿,包含中文和特殊字符:测试、验证、确认"
|
||||
size: "16:9"
|
||||
slides:
|
||||
- elements: []
|
||||
"""
|
||||
yaml_path = temp_dir / "test.yaml"
|
||||
yaml_path.write_text(yaml_content)
|
||||
|
||||
pres = Presentation(str(yaml_path))
|
||||
|
||||
assert "这是关于项目的演示文稿" in pres.description
|
||||
assert "中文" in pres.description
|
||||
|
||||
|
||||
class TestSlideDescription:
|
||||
"""Slide description 字段测试类"""
|
||||
|
||||
def test_slide_with_description(self, sample_yaml):
|
||||
"""测试幻灯片包含 description 字段时正确加载"""
|
||||
pres = Presentation(str(sample_yaml))
|
||||
|
||||
slide_data = {
|
||||
"description": "介绍项目背景和目标",
|
||||
"elements": [
|
||||
{"type": "text", "content": "Test", "box": [0, 0, 1, 1], "font": {}}
|
||||
]
|
||||
}
|
||||
|
||||
result = pres.render_slide(slide_data)
|
||||
|
||||
assert result["description"] == "介绍项目背景和目标"
|
||||
|
||||
def test_slide_without_description(self, sample_yaml):
|
||||
"""测试幻灯片不包含 description 字段时正常工作"""
|
||||
pres = Presentation(str(sample_yaml))
|
||||
|
||||
slide_data = {
|
||||
"elements": [
|
||||
{"type": "text", "content": "Test", "box": [0, 0, 1, 1], "font": {}}
|
||||
]
|
||||
}
|
||||
|
||||
result = pres.render_slide(slide_data)
|
||||
|
||||
assert result["description"] is None
|
||||
|
||||
def test_slide_description_empty_string(self, sample_yaml):
|
||||
"""测试幻灯片 description 为空字符串时正常工作"""
|
||||
pres = Presentation(str(sample_yaml))
|
||||
|
||||
slide_data = {
|
||||
"description": "",
|
||||
"elements": [
|
||||
{"type": "text", "content": "Test", "box": [0, 0, 1, 1], "font": {}}
|
||||
]
|
||||
}
|
||||
|
||||
result = pres.render_slide(slide_data)
|
||||
|
||||
assert result["description"] == ""
|
||||
|
||||
def test_slide_description_chinese_characters(self, sample_yaml):
|
||||
"""测试幻灯片 description 包含中文字符时正确处理"""
|
||||
pres = Presentation(str(sample_yaml))
|
||||
|
||||
slide_data = {
|
||||
"description": "这是幻灯片描述,包含中文内容",
|
||||
"elements": [
|
||||
{"type": "text", "content": "Test", "box": [0, 0, 1, 1], "font": {}}
|
||||
]
|
||||
}
|
||||
|
||||
result = pres.render_slide(slide_data)
|
||||
|
||||
assert "这是幻灯片描述" in result["description"]
|
||||
assert "中文" in result["description"]
|
||||
|
||||
def test_slide_description_does_not_affect_rendering(self, sample_yaml):
|
||||
"""测试 description 不影响渲染输出"""
|
||||
pres = Presentation(str(sample_yaml))
|
||||
|
||||
slide_data = {
|
||||
"description": "这段描述不应该影响渲染",
|
||||
"elements": [
|
||||
{"type": "text", "content": "Test Content", "box": [0, 0, 1, 1], "font": {}}
|
||||
]
|
||||
}
|
||||
|
||||
result = pres.render_slide(slide_data)
|
||||
|
||||
# description 字段存在但不影响元素渲染
|
||||
assert result["description"] == "这段描述不应该影响渲染"
|
||||
assert len(result["elements"]) == 1
|
||||
assert result["elements"][0].content == "Test Content"
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import pytest
|
||||
from pathlib import Path
|
||||
from loaders.yaml_loader import YAMLError
|
||||
from core.template import Template
|
||||
from core.presentation import Presentation
|
||||
|
||||
|
||||
# ============= 模板初始化测试 =============
|
||||
@@ -892,3 +893,165 @@ slides:
|
||||
slide_data = pres.data['slides'][0]
|
||||
rendered = pres.render_slide(slide_data)
|
||||
assert len(rendered['elements']) == 1
|
||||
|
||||
|
||||
# ============= Description 字段测试 =============
|
||||
|
||||
|
||||
class TestTemplateDescription:
|
||||
"""Template description 字段测试类"""
|
||||
|
||||
def test_template_with_description(self, temp_dir):
|
||||
"""测试模板包含 description 字段时正确加载"""
|
||||
template_content = """
|
||||
description: "用于章节标题页的模板,包含主标题和副标题"
|
||||
vars:
|
||||
- name: title
|
||||
required: true
|
||||
- name: subtitle
|
||||
required: false
|
||||
default: ""
|
||||
elements:
|
||||
- type: text
|
||||
content: "{title}"
|
||||
box: [1, 1, 8, 1]
|
||||
font:
|
||||
size: 44
|
||||
bold: true
|
||||
- type: text
|
||||
content: "{subtitle}"
|
||||
box: [1, 2, 8, 1]
|
||||
font:
|
||||
size: 24
|
||||
"""
|
||||
template_path = temp_dir / "test-template.yaml"
|
||||
template_path.write_text(template_content)
|
||||
|
||||
template = Template("test-template", templates_dir=temp_dir)
|
||||
|
||||
assert template.description == "用于章节标题页的模板,包含主标题和副标题"
|
||||
|
||||
def test_template_without_description(self, sample_template):
|
||||
"""测试模板不包含 description 字段时正常工作"""
|
||||
template = Template("title-slide", templates_dir=sample_template)
|
||||
|
||||
assert template.description is None
|
||||
|
||||
def test_template_description_empty_string(self, temp_dir):
|
||||
"""测试模板 description 为空字符串时正常工作"""
|
||||
template_content = """
|
||||
description: ""
|
||||
vars:
|
||||
- name: title
|
||||
elements:
|
||||
- type: text
|
||||
content: "{title}"
|
||||
box: [1, 1, 8, 1]
|
||||
font: {}
|
||||
"""
|
||||
template_path = temp_dir / "test-template.yaml"
|
||||
template_path.write_text(template_content)
|
||||
|
||||
template = Template("test-template", templates_dir=temp_dir)
|
||||
|
||||
assert template.description == ""
|
||||
|
||||
def test_template_description_chinese_characters(self, temp_dir):
|
||||
"""测试模板 description 包含中文字符时正确处理"""
|
||||
template_content = """
|
||||
description: "这是中文模板描述,用于标题页面"
|
||||
vars:
|
||||
- name: title
|
||||
elements:
|
||||
- type: text
|
||||
content: "{title}"
|
||||
box: [1, 1, 8, 1]
|
||||
font: {}
|
||||
"""
|
||||
template_path = temp_dir / "test-template.yaml"
|
||||
template_path.write_text(template_content)
|
||||
|
||||
template = Template("test-template", templates_dir=temp_dir)
|
||||
|
||||
assert "这是中文模板描述" in template.description
|
||||
assert "标题页面" in template.description
|
||||
|
||||
def test_template_description_multiline(self, temp_dir):
|
||||
"""测试模板 description 支持多行文本"""
|
||||
template_content = """
|
||||
description: |
|
||||
这是一个多行描述。
|
||||
第一行说明模板的用途。
|
||||
第二行说明使用场景。
|
||||
vars:
|
||||
- name: title
|
||||
elements:
|
||||
- type: text
|
||||
content: "{title}"
|
||||
box: [1, 1, 8, 1]
|
||||
font: {}
|
||||
"""
|
||||
template_path = temp_dir / "test-template.yaml"
|
||||
template_path.write_text(template_content)
|
||||
|
||||
template = Template("test-template", templates_dir=temp_dir)
|
||||
|
||||
# 多行文本应该被正确读取
|
||||
assert "这是一个多行描述" in template.description
|
||||
assert "第一行说明模板的用途" in template.description
|
||||
assert "第二行说明使用场景" in template.description
|
||||
|
||||
def test_inline_template_with_description(self, temp_dir):
|
||||
"""测试内联模板包含 description 字段"""
|
||||
yaml_content = """
|
||||
metadata:
|
||||
size: "16:9"
|
||||
|
||||
templates:
|
||||
test-template:
|
||||
description: "内联模板描述"
|
||||
vars:
|
||||
- name: title
|
||||
elements:
|
||||
- type: text
|
||||
content: "{title}"
|
||||
box: [1, 1, 8, 1]
|
||||
font: {}
|
||||
|
||||
slides:
|
||||
- template: test-template
|
||||
vars:
|
||||
title: "Test"
|
||||
"""
|
||||
yaml_path = temp_dir / "test.yaml"
|
||||
yaml_path.write_text(yaml_content)
|
||||
|
||||
pres = Presentation(str(yaml_path))
|
||||
template = pres.get_template("test-template")
|
||||
|
||||
assert template.description == "内联模板描述"
|
||||
|
||||
def test_template_description_does_not_affect_rendering(self, temp_dir):
|
||||
"""测试 description 不影响模板渲染"""
|
||||
template_content = """
|
||||
description: "这段描述不应该影响渲染"
|
||||
vars:
|
||||
- name: title
|
||||
elements:
|
||||
- type: text
|
||||
content: "{title}"
|
||||
box: [1, 1, 8, 1]
|
||||
font:
|
||||
size: 44
|
||||
"""
|
||||
template_path = temp_dir / "test-template.yaml"
|
||||
template_path.write_text(template_content)
|
||||
|
||||
template = Template("test-template", templates_dir=temp_dir)
|
||||
|
||||
# 渲染应该正常工作,description 不影响结果
|
||||
result = template.render({"title": "Test Title"})
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0]["content"] == "Test Title"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user