Refactor yaml2pptx.py from a 1,245-line monolithic script into a modular architecture with clear separation of concerns. The entry point is now 127 lines, with business logic distributed across focused modules. Architecture: - core/: Domain models (elements, template, presentation) - loaders/: YAML loading and validation - renderers/: PPTX and HTML rendering - preview/: Flask preview server - utils.py: Shared utilities Key improvements: - Element abstraction layer using dataclass with validation - Renderer logic built into generator classes - Single-direction dependencies (no circular imports) - Each module 150-300 lines for better readability - Backward compatible CLI interface Documentation: - README.md: User-facing usage guide - README_DEV.md: Developer documentation OpenSpec: - Archive refactor-yaml2pptx-modular change (63/70 tasks complete) - Sync 5 delta specs to main specs (2 new + 3 updated)
121 lines
4.8 KiB
Markdown
121 lines
4.8 KiB
Markdown
# Element Abstraction
|
||
|
||
## Purpose
|
||
|
||
定义元素抽象层的规格,使用 dataclass 实现类型安全的元素数据类,支持创建时验证和未来扩展。
|
||
|
||
## Requirements
|
||
|
||
### Requirement: 元素必须使用 dataclass 定义
|
||
|
||
系统必须使用 Python dataclass 定义所有元素类型(TextElement, ImageElement, ShapeElement, TableElement),提供类型安全和自动生成的方法。
|
||
|
||
#### Scenario: 文本元素使用 dataclass 定义
|
||
|
||
- **WHEN** 开发者查看 core/elements.py 中的 TextElement 类
|
||
- **THEN** 应使用 `@dataclass` 装饰器定义,包含 type、content、box、font 字段
|
||
|
||
#### Scenario: 图片元素使用 dataclass 定义
|
||
|
||
- **WHEN** 开发者查看 core/elements.py 中的 ImageElement 类
|
||
- **THEN** 应使用 `@dataclass` 装饰器定义,包含 type、src、box 字段
|
||
|
||
#### Scenario: 形状元素使用 dataclass 定义
|
||
|
||
- **WHEN** 开发者查看 core/elements.py 中的 ShapeElement 类
|
||
- **THEN** 应使用 `@dataclass` 装饰器定义,包含 type、shape、box、fill、line 字段
|
||
|
||
#### Scenario: 表格元素使用 dataclass 定义
|
||
|
||
- **WHEN** 开发者查看 core/elements.py 中的 TableElement 类
|
||
- **THEN** 应使用 `@dataclass` 装饰器定义,包含 type、data、position、col_widths、style 字段
|
||
|
||
### Requirement: 元素必须在创建时进行验证
|
||
|
||
系统必须在元素对象创建时(`__post_init__` 方法)进行数据验证,确保元素数据的有效性,尽早发现错误。
|
||
|
||
#### Scenario: 文本元素验证 box 字段
|
||
|
||
- **WHEN** 创建 TextElement 对象时 box 字段不是包含 4 个数字的列表
|
||
- **THEN** 系统应抛出 ValueError 异常,提示 "box 必须包含 4 个数字"
|
||
|
||
#### Scenario: 图片元素验证 src 字段
|
||
|
||
- **WHEN** 创建 ImageElement 对象时 src 字段为空
|
||
- **THEN** 系统应抛出 ValueError 异常,提示 "图片元素必须指定 src"
|
||
|
||
#### Scenario: 形状元素验证 box 字段
|
||
|
||
- **WHEN** 创建 ShapeElement 对象时 box 字段不是包含 4 个数字的列表
|
||
- **THEN** 系统应抛出 ValueError 异常,提示 "box 必须包含 4 个数字"
|
||
|
||
#### Scenario: 表格元素验证 data 字段
|
||
|
||
- **WHEN** 创建 TableElement 对象时 data 字段为空列表
|
||
- **THEN** 系统应抛出 ValueError 异常,提示 "表格数据不能为空"
|
||
|
||
### Requirement: 必须提供元素工厂函数
|
||
|
||
系统必须提供 `create_element(elem_dict)` 工厂函数,从字典创建对应类型的元素对象,统一元素创建入口。
|
||
|
||
#### Scenario: 从字典创建文本元素
|
||
|
||
- **WHEN** 调用 `create_element({'type': 'text', 'content': 'Hello', 'box': [1, 1, 8, 1]})`
|
||
- **THEN** 系统应返回 TextElement 对象,包含相应的字段值
|
||
|
||
#### Scenario: 从字典创建图片元素
|
||
|
||
- **WHEN** 调用 `create_element({'type': 'image', 'src': 'image.png', 'box': [1, 1, 4, 3]})`
|
||
- **THEN** 系统应返回 ImageElement 对象,包含相应的字段值
|
||
|
||
#### Scenario: 从字典创建形状元素
|
||
|
||
- **WHEN** 调用 `create_element({'type': 'shape', 'shape': 'rectangle', 'box': [1, 1, 2, 1]})`
|
||
- **THEN** 系统应返回 ShapeElement 对象,包含相应的字段值
|
||
|
||
#### Scenario: 从字典创建表格元素
|
||
|
||
- **WHEN** 调用 `create_element({'type': 'table', 'data': [['A', 'B'], ['1', '2']], 'position': [1, 1]})`
|
||
- **THEN** 系统应返回 TableElement 对象,包含相应的字段值
|
||
|
||
#### Scenario: 不支持的元素类型
|
||
|
||
- **WHEN** 调用 `create_element({'type': 'unknown'})`
|
||
- **THEN** 系统应抛出异常,提示不支持的元素类型
|
||
|
||
### Requirement: 元素类型必须支持未来扩展
|
||
|
||
系统的元素抽象层设计必须支持未来添加新元素类型(如 VideoElement),只需定义新的 dataclass 和在工厂函数中添加分支。
|
||
|
||
#### Scenario: 添加新元素类型的步骤清晰
|
||
|
||
- **WHEN** 开发者需要添加新元素类型(如 Video)
|
||
- **THEN** 应只需要:
|
||
1. 在 core/elements.py 中定义新的 dataclass(如 VideoElement)
|
||
2. 在 `create_element()` 工厂函数中添加对应的分支
|
||
3. 在各个渲染器中实现渲染方法
|
||
|
||
#### Scenario: 新元素类型不影响现有元素
|
||
|
||
- **WHEN** 添加新元素类型
|
||
- **THEN** 现有元素类型(Text, Image, Shape, Table)的行为不应受到影响
|
||
|
||
### Requirement: 元素数据类必须提供默认值
|
||
|
||
系统必须为元素数据类的可选字段提供合理的默认值,简化元素创建。
|
||
|
||
#### Scenario: 文本元素的默认值
|
||
|
||
- **WHEN** 创建 TextElement 对象时不提供 box 和 font 字段
|
||
- **THEN** 系统应使用默认值:box=[1, 1, 8, 1],font={}
|
||
|
||
#### Scenario: 图片元素的默认值
|
||
|
||
- **WHEN** 创建 ImageElement 对象时不提供 box 字段
|
||
- **THEN** 系统应使用默认值:box=[1, 1, 4, 3]
|
||
|
||
#### Scenario: 形状元素的默认值
|
||
|
||
- **WHEN** 创建 ShapeElement 对象时不提供 fill 和 line 字段
|
||
- **THEN** 系统应使用默认值或 None
|