1
0
Files
lanyuanxiaoyao ed940f0690 refactor: modularize yaml2pptx into layered architecture
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)
2026-03-02 16:43:45 +08:00

121 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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