feat: add YAML validation with check command and auto-validation
Implements comprehensive validation before PPTX conversion to catch errors early. Includes element-level validation (colors, fonts, table consistency) and system-level validation (geometry, resources). Supports standalone check command and automatic validation during conversion.
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-03-02
|
||||
@@ -0,0 +1,284 @@
|
||||
## Context
|
||||
|
||||
当前 yaml2pptx 系统已经有基本的 YAML 加载和元素验证功能:
|
||||
- `loaders/yaml_loader.py` 提供基本的 YAML 语法检查和结构验证
|
||||
- `core/elements.py` 定义了元素数据类,在 `__post_init__` 中进行基本验证
|
||||
|
||||
但现有验证不够全面,用户经常在转换后才发现问题(元素超出页面、文件不存在等),需要反复修改和转换。
|
||||
|
||||
项目约束:
|
||||
- 使用 uv 运行 Python 脚本
|
||||
- 无新增外部依赖
|
||||
- 测试文件放在 temp 目录
|
||||
- 面向中文开发者
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 提供全面的 YAML 验证功能,在转换前发现问题
|
||||
- 支持独立 check 命令和转换前自动检查
|
||||
- 实现分级错误报告(ERROR/WARNING/INFO)
|
||||
- 保持验证逻辑的可扩展性和可维护性
|
||||
|
||||
**Non-Goals:**
|
||||
- 不实现自动修复功能(只检测,不修改)
|
||||
- 不支持第三阶段的质量警告(元素重叠、文本溢出等)
|
||||
- 不添加 GUI 或 Web 界面
|
||||
- 不实现配置文件来自定义验证规则
|
||||
|
||||
## Decisions
|
||||
|
||||
### 决策 1:验证职责分层
|
||||
|
||||
**决策**:将验证逻辑分为两层:
|
||||
1. **元素级验证**:放在元素类本身(`core/elements.py`)
|
||||
2. **系统级验证**:放在独立的验证器模块(`validators/`)
|
||||
|
||||
**理由**:
|
||||
- 元素类最了解自己的约束,应该负责自身的完整性验证
|
||||
- 系统级验证需要全局上下文(如页面尺寸、文件路径),适合集中处理
|
||||
- 符合单一职责原则,便于扩展和维护
|
||||
|
||||
**元素级验证职责**:
|
||||
- 必需字段检查(如 image 必须有 src)
|
||||
- 数据类型检查(如 box 必须是 4 个数字)
|
||||
- 值的有效性检查(如颜色格式、枚举值)
|
||||
- 元素内部逻辑一致性
|
||||
|
||||
**系统级验证职责**:
|
||||
- 几何验证(元素是否在页面范围内,需要知道页面尺寸)
|
||||
- 资源验证(文件是否存在,需要知道文件路径)
|
||||
- 跨元素验证(如果未来需要)
|
||||
|
||||
**替代方案**:
|
||||
- 方案 A:所有验证集中在验证器中 → 验证器会变得臃肿,元素类失去封装性
|
||||
- 方案 B:所有验证都在元素类中 → 元素类需要知道全局上下文,违反依赖倒置原则
|
||||
|
||||
### 决策 2:验证器模块结构
|
||||
|
||||
**决策**:创建 `validators/` 目录,包含以下模块:
|
||||
```
|
||||
validators/
|
||||
├── __init__.py # 导出主验证器
|
||||
├── validator.py # 主验证器,协调各子验证器
|
||||
├── geometry.py # 几何验证器
|
||||
├── resource.py # 资源验证器
|
||||
└── result.py # 验证结果数据结构
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 模块化设计,每个验证器职责单一
|
||||
- 便于测试和扩展
|
||||
- 主验证器作为门面,简化调用
|
||||
|
||||
**验证流程**:
|
||||
```
|
||||
Validator.validate(yaml_path, template_dir)
|
||||
↓
|
||||
1. 加载 YAML(复用 yaml_loader)
|
||||
2. 元素级验证(调用元素类的验证方法)
|
||||
3. 几何验证(GeometryValidator)
|
||||
4. 资源验证(ResourceValidator)
|
||||
↓
|
||||
返回 ValidationResult
|
||||
```
|
||||
|
||||
**替代方案**:
|
||||
- 方案 A:单一验证器文件 → 代码过长,难以维护
|
||||
- 方案 B:每种验证一个独立包 → 过度设计,增加复杂度
|
||||
|
||||
### 决策 3:验证结果数据结构
|
||||
|
||||
**决策**:定义 `ValidationResult` 和 `ValidationIssue` 类:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class ValidationIssue:
|
||||
level: str # "ERROR" | "WARNING" | "INFO"
|
||||
message: str
|
||||
location: str # "幻灯片 2, 元素 3"
|
||||
code: str # "ELEMENT_OUT_OF_BOUNDS"
|
||||
|
||||
@dataclass
|
||||
class ValidationResult:
|
||||
valid: bool # 是否有 ERROR
|
||||
errors: List[ValidationIssue]
|
||||
warnings: List[ValidationIssue]
|
||||
infos: List[ValidationIssue]
|
||||
|
||||
def has_errors(self) -> bool
|
||||
def format_output(self) -> str # 格式化为命令行输出
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 结构化的结果便于测试和扩展
|
||||
- `code` 字段便于未来实现错误码查询或国际化
|
||||
- `format_output()` 方法封装输出格式,便于修改
|
||||
|
||||
**替代方案**:
|
||||
- 方案 A:直接返回字符串 → 难以测试,不便于扩展
|
||||
- 方案 B:返回字典 → 缺少类型安全,容易出错
|
||||
|
||||
### 决策 4:命令行接口设计
|
||||
|
||||
**决策**:修改 `yaml2pptx.py`,使用子命令模式:
|
||||
|
||||
```python
|
||||
# 独立验证
|
||||
yaml2pptx.py check input.yaml [--template-dir DIR]
|
||||
|
||||
# 转换(默认自动验证)
|
||||
yaml2pptx.py input.yaml [output.pptx] [--template-dir DIR] [--no-check]
|
||||
```
|
||||
|
||||
**实现方式**:
|
||||
- 使用 `argparse` 的 `subparsers` 实现子命令
|
||||
- `check` 子命令调用验证器并输出结果
|
||||
- 转换命令在加载 YAML 后、渲染前调用验证器
|
||||
- 如果验证失败(有 ERROR),终止转换并返回非零退出码
|
||||
|
||||
**理由**:
|
||||
- 子命令模式清晰,符合 CLI 工具惯例
|
||||
- 默认启用自动验证,提升用户体验
|
||||
- `--no-check` 提供灵活性,适合调试场景
|
||||
|
||||
**替代方案**:
|
||||
- 方案 A:独立的 `yaml2pptx-check` 命令 → 增加维护成本,用户需要记住两个命令
|
||||
- 方案 B:只有自动验证,没有独立命令 → 用户无法单独验证
|
||||
|
||||
### 决策 5:元素类验证方法增强
|
||||
|
||||
**决策**:在 `core/elements.py` 中为每个元素类添加 `validate()` 方法:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class TextElement:
|
||||
# ... 现有字段 ...
|
||||
|
||||
def validate(self) -> List[ValidationIssue]:
|
||||
"""验证元素自身的完整性"""
|
||||
issues = []
|
||||
|
||||
# 检查颜色格式
|
||||
if self.font.get('color'):
|
||||
if not self._is_valid_color(self.font['color']):
|
||||
issues.append(ValidationIssue(
|
||||
level="ERROR",
|
||||
message=f"无效的颜色格式: {self.font['color']}",
|
||||
location="", # 由调用者填充
|
||||
code="INVALID_COLOR_FORMAT"
|
||||
))
|
||||
|
||||
# 检查字体大小
|
||||
if self.font.get('size'):
|
||||
size = self.font['size']
|
||||
if size < 8:
|
||||
issues.append(ValidationIssue(
|
||||
level="WARNING",
|
||||
message=f"字体太小: {size}pt (建议 >= 8pt)",
|
||||
location="",
|
||||
code="FONT_TOO_SMALL"
|
||||
))
|
||||
|
||||
return issues
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 元素类封装自己的验证逻辑
|
||||
- `__post_init__` 保留用于阻止创建无效对象(如 box 不是 4 个数字)
|
||||
- `validate()` 用于检测可以创建但不推荐的情况(如字体太小)
|
||||
|
||||
**替代方案**:
|
||||
- 方案 A:只在 `__post_init__` 中验证 → 无法区分致命错误和警告
|
||||
- 方案 B:不修改元素类,所有验证在验证器中 → 违反封装原则
|
||||
|
||||
### 决策 6:边界检查容忍度
|
||||
|
||||
**决策**:几何验证时,允许 0.1 英寸的容忍度:
|
||||
|
||||
```python
|
||||
TOLERANCE = 0.1 # 英寸
|
||||
|
||||
if right > slide_width + TOLERANCE:
|
||||
# 报告 WARNING
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 浮点数计算可能有精度误差
|
||||
- 0.1 英寸(约 2.54mm)在视觉上几乎不可见
|
||||
- 避免误报,提升用户体验
|
||||
|
||||
**替代方案**:
|
||||
- 方案 A:零容忍 → 可能产生大量误报
|
||||
- 方案 B:更大的容忍度(如 0.5 英寸)→ 可能漏掉真正的问题
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
### 风险 1:验证性能影响
|
||||
|
||||
**风险**:验证可能增加转换时间,特别是资源验证(检查文件存在性)。
|
||||
|
||||
**缓解措施**:
|
||||
- 验证是可选的(`--no-check` 跳过)
|
||||
- 资源验证只检查文件存在性,不读取文件内容
|
||||
- 未来可以考虑并行验证(如果性能成为问题)
|
||||
|
||||
### 风险 2:元素类验证方法的维护成本
|
||||
|
||||
**风险**:每个元素类都需要实现 `validate()` 方法,增加维护成本。
|
||||
|
||||
**缓解措施**:
|
||||
- 提供基类或工具函数来复用常见验证逻辑(如颜色格式检查)
|
||||
- 验证逻辑相对稳定,不会频繁修改
|
||||
- 好处是验证逻辑和元素定义在一起,便于理解和修改
|
||||
|
||||
### 风险 3:错误消息的可读性
|
||||
|
||||
**风险**:错误消息可能不够清晰,用户难以理解如何修复。
|
||||
|
||||
**缓解措施**:
|
||||
- 错误消息包含具体的位置信息(幻灯片、元素)
|
||||
- 错误消息包含期望值和实际值(如 "right=10.5 > 10.0")
|
||||
- 未来可以添加错误码和文档链接
|
||||
|
||||
### Trade-off:验证完整性 vs 性能
|
||||
|
||||
**选择**:优先保证验证完整性,性能其次。
|
||||
|
||||
**理由**:
|
||||
- 验证是可选的,性能敏感的场景可以跳过
|
||||
- 提前发现问题比快速转换更重要
|
||||
- 当前验证项不多,性能影响可控
|
||||
|
||||
## Migration Plan
|
||||
|
||||
**部署步骤**:
|
||||
1. 实现验证器模块(不影响现有功能)
|
||||
2. 增强元素类的验证方法(向后兼容)
|
||||
3. 修改 `yaml2pptx.py` 添加 check 子命令
|
||||
4. 添加自动验证逻辑(默认启用)
|
||||
5. 编写测试用例(在 temp 目录)
|
||||
6. 更新 README.md 文档
|
||||
|
||||
**回滚策略**:
|
||||
- 如果验证器有问题,用户可以使用 `--no-check` 跳过
|
||||
- 验证器是独立模块,可以快速禁用或修复
|
||||
- 不影响现有的转换功能
|
||||
|
||||
**兼容性**:
|
||||
- 完全向后兼容,现有 YAML 文件和命令行用法不受影响
|
||||
- 新增的 check 子命令和 --no-check 选项是可选的
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **是否需要配置文件来自定义验证规则?**
|
||||
- 当前决策:不需要,保持简单
|
||||
- 未来可以考虑添加 `.yaml2pptx.yaml` 配置文件
|
||||
|
||||
2. **是否需要支持 JSON 格式的验证结果输出?**
|
||||
- 当前决策:只支持命令行文本输出
|
||||
- 未来可以添加 `--format json` 选项
|
||||
|
||||
3. **字体大小的合理范围是否需要可配置?**
|
||||
- 当前决策:硬编码 8pt-100pt
|
||||
- 未来可以考虑通过配置文件自定义
|
||||
@@ -0,0 +1,40 @@
|
||||
## Why
|
||||
|
||||
用户在编写 YAML 演示文稿源文件时,经常遇到元素超出页面范围、图片文件路径错误、语法问题等错误,这些问题只有在转换成 PPTX 后打开查看才能发现,导致需要反复修改和转换,效率低下。需要提供一个验证功能,在转换前检查 YAML 文件的各种潜在问题,让用户能够提前发现并修复错误。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 添加 YAML 文件验证功能,支持多层次的检查
|
||||
- 结构验证:必需字段、数据类型、枚举值
|
||||
- 几何验证:元素边界、页面范围检查
|
||||
- 资源验证:图片文件、模板文件存在性
|
||||
- 样式验证:颜色格式、字体大小合理性
|
||||
- 添加独立的 `check` 子命令:`yaml2pptx.py check input.yaml`
|
||||
- 添加转换前自动检查选项:默认启用,可通过 `--no-check` 跳过
|
||||
- 实现分级的错误报告系统
|
||||
- ERROR:阻止转换的严重问题(文件不存在、语法错误、关键参数缺失)
|
||||
- WARNING:影响视觉效果的问题(元素超出页面、字体太小)
|
||||
- INFO:优化建议
|
||||
- 输出清晰的命令行报告,显示问题位置(幻灯片、元素)和具体描述
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `yaml-validation`: YAML 演示文稿文件的验证能力,包括结构验证、几何验证、资源验证和样式验证
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。这是新增功能,不修改现有能力的需求。
|
||||
|
||||
## Impact
|
||||
|
||||
- **主程序**:修改 `yaml2pptx.py`,添加 `check` 子命令和 `--no-check` 选项
|
||||
- **新增模块**:创建验证器模块(建议在 `validators/` 目录下)
|
||||
- 结构验证器
|
||||
- 几何验证器
|
||||
- 资源验证器
|
||||
- 样式验证器
|
||||
- **现有功能**:不影响现有的转换功能,验证是可选的
|
||||
- **依赖**:无新增外部依赖,使用现有的 PyYAML 和标准库
|
||||
- **用户体验**:提升开发效率,减少调试时间
|
||||
@@ -0,0 +1,187 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 独立验证命令
|
||||
|
||||
系统必须提供独立的 check 子命令,用于验证 YAML 文件而不执行转换。
|
||||
|
||||
#### Scenario: 执行独立验证
|
||||
|
||||
- **WHEN** 用户运行 `yaml2pptx.py check input.yaml`
|
||||
- **THEN** 系统验证 YAML 文件并输出验证结果,不生成 PPTX 文件
|
||||
|
||||
#### Scenario: 验证时指定模板目录
|
||||
|
||||
- **WHEN** 用户运行 `yaml2pptx.py check input.yaml --template-dir ./templates`
|
||||
- **THEN** 系统在验证时使用指定的模板目录来检查模板文件
|
||||
|
||||
### Requirement: 转换前自动验证
|
||||
|
||||
系统必须在转换 YAML 为 PPTX 之前自动执行验证,除非用户明确跳过。
|
||||
|
||||
#### Scenario: 默认自动验证
|
||||
|
||||
- **WHEN** 用户运行 `yaml2pptx.py input.yaml output.pptx`
|
||||
- **THEN** 系统在转换前自动验证 YAML 文件
|
||||
|
||||
#### Scenario: 跳过自动验证
|
||||
|
||||
- **WHEN** 用户运行 `yaml2pptx.py input.yaml output.pptx --no-check`
|
||||
- **THEN** 系统跳过验证,直接执行转换
|
||||
|
||||
#### Scenario: 验证失败阻止转换
|
||||
|
||||
- **WHEN** 自动验证发现 ERROR 级别的问题
|
||||
- **THEN** 系统输出错误信息并终止转换,返回非零退出码
|
||||
|
||||
### Requirement: 元素自验证
|
||||
|
||||
系统中的每个元素类型必须能够验证自身的完整性和有效性。
|
||||
|
||||
#### Scenario: 元素验证自身属性
|
||||
|
||||
- **WHEN** 创建或验证元素对象
|
||||
- **THEN** 元素类检查自身的必需字段、数据类型、值有效性(如颜色格式、枚举值、字体大小)
|
||||
|
||||
#### Scenario: 元素验证与系统验证分离
|
||||
|
||||
- **WHEN** 执行验证
|
||||
- **THEN** 元素类负责自身属性验证(不需要全局上下文的验证),系统验证器负责需要全局上下文的验证(如几何验证需要页面尺寸、资源验证需要文件路径)
|
||||
|
||||
### Requirement: 结构验证
|
||||
|
||||
系统必须验证 YAML 文件的结构完整性和数据类型正确性。
|
||||
|
||||
#### Scenario: 检测缺少必需字段
|
||||
|
||||
- **WHEN** YAML 文件缺少 slides 字段
|
||||
- **THEN** 系统报告 ERROR 级别错误:"缺少必需字段 'slides'"
|
||||
|
||||
#### Scenario: 检测数据类型错误
|
||||
|
||||
- **WHEN** slides 字段不是列表类型
|
||||
- **THEN** 系统报告 ERROR 级别错误:"'slides' 必须是一个列表"
|
||||
|
||||
#### Scenario: 检测元素类型无效
|
||||
|
||||
- **WHEN** 元素的 type 字段值不是 text/image/shape/table 之一
|
||||
- **THEN** 系统报告 ERROR 级别错误:"不支持的元素类型: {type}"
|
||||
|
||||
#### Scenario: 检测无效的枚举值
|
||||
|
||||
- **WHEN** shape 元素的 shape 字段值不是 rectangle/ellipse/rounded_rectangle 之一
|
||||
- **THEN** 系统报告 ERROR 级别错误:"不支持的形状类型: {shape}"
|
||||
|
||||
### Requirement: 几何验证
|
||||
|
||||
系统必须验证元素的位置和尺寸是否在页面范围内。
|
||||
|
||||
#### Scenario: 检测元素超出页面右边界
|
||||
|
||||
- **WHEN** 元素的 left + width 超出页面宽度 0.1 英寸以上
|
||||
- **THEN** 系统报告 WARNING 级别警告:"元素右边界超出: {right} > {width}"
|
||||
|
||||
#### Scenario: 检测元素超出页面下边界
|
||||
|
||||
- **WHEN** 元素的 top + height 超出页面高度 0.1 英寸以上
|
||||
- **THEN** 系统报告 WARNING 级别警告:"元素下边界超出: {bottom} > {height}"
|
||||
|
||||
#### Scenario: 容忍计算精度误差
|
||||
|
||||
- **WHEN** 元素的边界超出页面范围不超过 0.1 英寸
|
||||
- **THEN** 系统不报告警告
|
||||
|
||||
#### Scenario: 检测元素完全在页面外
|
||||
|
||||
- **WHEN** 元素的 left >= 页面宽度 或 top >= 页面高度
|
||||
- **THEN** 系统报告 WARNING 级别警告:"元素完全在页面外"
|
||||
|
||||
#### Scenario: 检测表格超出页面范围
|
||||
|
||||
- **WHEN** 表格的 position[0] + sum(col_widths) 超出页面宽度 0.1 英寸以上
|
||||
- **THEN** 系统报告 WARNING 级别警告:"表格超出页面宽度"
|
||||
|
||||
### Requirement: 资源验证
|
||||
|
||||
系统必须验证 YAML 文件引用的外部资源是否存在。
|
||||
|
||||
#### Scenario: 检测图片文件不存在
|
||||
|
||||
- **WHEN** image 元素的 src 指向的文件不存在
|
||||
- **THEN** 系统报告 ERROR 级别错误:"图片文件不存在: {src}"
|
||||
|
||||
#### Scenario: 支持相对路径图片
|
||||
|
||||
- **WHEN** image 元素的 src 是相对路径
|
||||
- **THEN** 系统相对于 YAML 文件所在目录解析路径
|
||||
|
||||
#### Scenario: 检测模板文件不存在
|
||||
|
||||
- **WHEN** 幻灯片引用的 template 文件不存在
|
||||
- **THEN** 系统报告 ERROR 级别错误:"模板文件不存在: {template}"
|
||||
|
||||
#### Scenario: 验证模板文件结构
|
||||
|
||||
- **WHEN** 模板文件存在但结构不正确(缺少 elements 字段)
|
||||
- **THEN** 系统报告 ERROR 级别错误:"模板文件结构错误: {template}"
|
||||
|
||||
### Requirement: 样式验证
|
||||
|
||||
系统必须验证样式相关属性的有效性和合理性。
|
||||
|
||||
#### Scenario: 检测无效的颜色格式
|
||||
|
||||
- **WHEN** 颜色值不符合 #RGB 或 #RRGGBB 格式
|
||||
- **THEN** 系统报告 ERROR 级别错误:"无效的颜色格式: {color}"
|
||||
|
||||
#### Scenario: 检测字体过小
|
||||
|
||||
- **WHEN** 字体大小小于 8pt
|
||||
- **THEN** 系统报告 WARNING 级别警告:"字体太小: {size}pt (建议 >= 8pt)"
|
||||
|
||||
#### Scenario: 检测字体过大
|
||||
|
||||
- **WHEN** 字体大小大于 100pt
|
||||
- **THEN** 系统报告 WARNING 级别警告:"字体太大: {size}pt (建议 <= 100pt)"
|
||||
|
||||
### Requirement: 分级错误报告
|
||||
|
||||
系统必须按照严重程度对验证问题进行分级报告。
|
||||
|
||||
#### Scenario: ERROR 级别阻止转换
|
||||
|
||||
- **WHEN** 验证发现 ERROR 级别问题
|
||||
- **THEN** 系统在独立验证模式下返回非零退出码,在自动验证模式下阻止转换
|
||||
|
||||
#### Scenario: WARNING 级别不阻止转换
|
||||
|
||||
- **WHEN** 验证仅发现 WARNING 级别问题
|
||||
- **THEN** 系统输出警告信息但允许转换继续,返回零退出码
|
||||
|
||||
#### Scenario: 显示问题位置
|
||||
|
||||
- **WHEN** 验证发现问题
|
||||
- **THEN** 系统输出包含幻灯片编号和元素编号的位置信息:"[幻灯片 {n}, 元素 {m}]"
|
||||
|
||||
#### Scenario: 分类统计问题数量
|
||||
|
||||
- **WHEN** 验证完成
|
||||
- **THEN** 系统输出问题统计:"发现 {n} 个错误, {m} 个警告"
|
||||
|
||||
### Requirement: 清晰的命令行输出
|
||||
|
||||
系统必须以清晰易读的格式输出验证结果。
|
||||
|
||||
#### Scenario: 使用图标区分级别
|
||||
|
||||
- **WHEN** 输出验证结果
|
||||
- **THEN** 系统使用 "❌" 表示 ERROR,"⚠️" 表示 WARNING,"ℹ️" 表示 INFO
|
||||
|
||||
#### Scenario: 按级别分组显示
|
||||
|
||||
- **WHEN** 输出验证结果
|
||||
- **THEN** 系统按 ERROR、WARNING、INFO 分组显示问题
|
||||
|
||||
#### Scenario: 验证通过的提示
|
||||
|
||||
- **WHEN** 验证未发现任何问题
|
||||
- **THEN** 系统输出:"✅ 验证通过,未发现问题"
|
||||
@@ -0,0 +1,75 @@
|
||||
## 1. 验证结果数据结构
|
||||
|
||||
- [x] 1.1 创建 validators/ 目录结构
|
||||
- [x] 1.2 实现 validators/result.py 中的 ValidationIssue 数据类
|
||||
- [x] 1.3 实现 validators/result.py 中的 ValidationResult 数据类
|
||||
- [x] 1.4 实现 ValidationResult.has_errors() 方法
|
||||
- [x] 1.5 实现 ValidationResult.format_output() 方法,支持图标和分组显示
|
||||
|
||||
## 2. 元素类验证增强
|
||||
|
||||
- [x] 2.1 在 core/elements.py 中添加颜色格式验证工具函数 _is_valid_color()
|
||||
- [x] 2.2 为 TextElement 类添加 validate() 方法,检查颜色格式和字体大小
|
||||
- [x] 2.3 为 ImageElement 类添加 validate() 方法,检查必需字段
|
||||
- [x] 2.4 为 ShapeElement 类添加 validate() 方法,检查颜色格式和形状类型枚举
|
||||
- [x] 2.5 为 TableElement 类添加 validate() 方法,检查数据完整性
|
||||
|
||||
## 3. 几何验证器
|
||||
|
||||
- [x] 3.1 创建 validators/geometry.py 文件
|
||||
- [x] 3.2 实现 GeometryValidator 类
|
||||
- [x] 3.3 实现元素边界检查方法,支持 0.1 英寸容忍度
|
||||
- [x] 3.4 实现表格边界检查方法
|
||||
- [x] 3.5 实现元素完全在页面外的检查
|
||||
|
||||
## 4. 资源验证器
|
||||
|
||||
- [x] 4.1 创建 validators/resource.py 文件
|
||||
- [x] 4.2 实现 ResourceValidator 类
|
||||
- [x] 4.3 实现图片文件存在性检查,支持相对路径和绝对路径
|
||||
- [x] 4.4 实现模板文件存在性检查
|
||||
- [x] 4.5 实现模板文件结构验证(检查 elements 字段)
|
||||
|
||||
## 5. 主验证器
|
||||
|
||||
- [x] 5.1 创建 validators/validator.py 文件
|
||||
- [x] 5.2 实现 Validator 类的 validate() 方法
|
||||
- [x] 5.3 集成 YAML 加载(复用 loaders/yaml_loader.py)
|
||||
- [x] 5.4 集成元素级验证(调用元素类的 validate() 方法)
|
||||
- [x] 5.5 集成几何验证器
|
||||
- [x] 5.6 集成资源验证器
|
||||
- [x] 5.7 在 validators/__init__.py 中导出主验证器
|
||||
|
||||
## 6. 命令行接口 - check 子命令
|
||||
|
||||
- [x] 6.1 修改 yaml2pptx.py,使用 argparse 的 subparsers 添加 check 子命令
|
||||
- [x] 6.2 实现 check 子命令的参数解析(input, --template-dir)
|
||||
- [x] 6.3 实现 check 子命令的处理函数,调用验证器
|
||||
- [x] 6.4 实现验证结果的命令行输出
|
||||
- [x] 6.5 根据验证结果返回正确的退出码(有 ERROR 返回非零)
|
||||
|
||||
## 7. 命令行接口 - 自动验证
|
||||
|
||||
- [x] 7.1 在转换命令中添加 --no-check 选项
|
||||
- [x] 7.2 在转换流程中添加自动验证逻辑(加载 YAML 后、渲染前)
|
||||
- [x] 7.3 实现验证失败时终止转换的逻辑
|
||||
- [x] 7.4 确保 --no-check 选项可以跳过验证
|
||||
|
||||
## 8. 测试
|
||||
|
||||
- [x] 8.1 在 temp/ 目录创建测试用的 YAML 文件(包含各种错误场景)
|
||||
- [x] 8.2 测试独立 check 命令的各种场景
|
||||
- [x] 8.3 测试自动验证功能
|
||||
- [x] 8.4 测试 --no-check 选项
|
||||
- [x] 8.5 测试元素超出页面边界的检测
|
||||
- [x] 8.6 测试图片文件不存在的检测
|
||||
- [x] 8.7 测试模板文件验证
|
||||
- [x] 8.8 测试颜色格式验证
|
||||
- [x] 8.9 测试字体大小警告
|
||||
- [x] 8.10 测试容忍度(0.1 英寸)
|
||||
|
||||
## 9. 文档更新
|
||||
|
||||
- [x] 9.1 更新 README.md,添加 check 命令的使用说明
|
||||
- [x] 9.2 更新 README.md,说明自动验证功能和 --no-check 选项
|
||||
- [x] 9.3 添加验证错误码和消息的说明(如果需要)
|
||||
@@ -2,9 +2,9 @@ schema: spec-driven
|
||||
|
||||
context: |
|
||||
本项目始终面向中文开发者,使用中文进行注释、交流等内容的处理,不考虑多语言;
|
||||
本项目编写的python脚本始终使用uv运行,脚本使用Inline script metadata来指定脚本的依赖包和python运行版本;
|
||||
严禁直接使用主机环境的python直接执行脚本,严禁在主机环境直接安装python依赖包;
|
||||
本项目编写的python脚本和任何python命令都始终使用uv运行,脚本使用Inline script metadata来指定脚本的依赖包和python运行版本,命令使用uv run python -c "xxx"执行命令;
|
||||
严禁直接使用主机环境的python直接执行脚本或命令,严禁在主机环境直接安装python依赖包;
|
||||
本项目编写的测试文件、临时文件必须放在temp目录下;
|
||||
严禁污染主机环境的任何配置,如有需要,必须请求用户审核操作;
|
||||
当前项目的面向用户的使用文档在README.md;
|
||||
当前项目的面向AI和开发者的开发规范文档在README_DEV.md;
|
||||
当前项目的面向AI和开发者的开发规范文档在README_DEV.md;
|
||||
|
||||
193
openspec/specs/yaml-validation/spec.md
Normal file
193
openspec/specs/yaml-validation/spec.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# YAML Validation
|
||||
|
||||
## Purpose
|
||||
|
||||
提供 YAML 演示文稿文件的验证能力,在转换为 PPTX 之前检查各种潜在问题,包括结构验证、几何验证、资源验证和样式验证。支持独立验证命令和转换前自动验证。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: 独立验证命令
|
||||
|
||||
系统必须提供独立的 check 子命令,用于验证 YAML 文件而不执行转换。
|
||||
|
||||
#### Scenario: 执行独立验证
|
||||
|
||||
- **WHEN** 用户运行 `yaml2pptx.py check input.yaml`
|
||||
- **THEN** 系统验证 YAML 文件并输出验证结果,不生成 PPTX 文件
|
||||
|
||||
#### Scenario: 验证时指定模板目录
|
||||
|
||||
- **WHEN** 用户运行 `yaml2pptx.py check input.yaml --template-dir ./templates`
|
||||
- **THEN** 系统在验证时使用指定的模板目录来检查模板文件
|
||||
|
||||
### Requirement: 转换前自动验证
|
||||
|
||||
系统必须在转换 YAML 为 PPTX 之前自动执行验证,除非用户明确跳过。
|
||||
|
||||
#### Scenario: 默认自动验证
|
||||
|
||||
- **WHEN** 用户运行 `yaml2pptx.py input.yaml output.pptx`
|
||||
- **THEN** 系统在转换前自动验证 YAML 文件
|
||||
|
||||
#### Scenario: 跳过自动验证
|
||||
|
||||
- **WHEN** 用户运行 `yaml2pptx.py input.yaml output.pptx --no-check`
|
||||
- **THEN** 系统跳过验证,直接执行转换
|
||||
|
||||
#### Scenario: 验证失败阻止转换
|
||||
|
||||
- **WHEN** 自动验证发现 ERROR 级别的问题
|
||||
- **THEN** 系统输出错误信息并终止转换,返回非零退出码
|
||||
|
||||
### Requirement: 元素自验证
|
||||
|
||||
系统中的每个元素类型必须能够验证自身的完整性和有效性。
|
||||
|
||||
#### Scenario: 元素验证自身属性
|
||||
|
||||
- **WHEN** 创建或验证元素对象
|
||||
- **THEN** 元素类检查自身的必需字段、数据类型、值有效性(如颜色格式、枚举值、字体大小)
|
||||
|
||||
#### Scenario: 元素验证与系统验证分离
|
||||
|
||||
- **WHEN** 执行验证
|
||||
- **THEN** 元素类负责自身属性验证(不需要全局上下文的验证),系统验证器负责需要全局上下文的验证(如几何验证需要页面尺寸、资源验证需要文件路径)
|
||||
|
||||
### Requirement: 结构验证
|
||||
|
||||
系统必须验证 YAML 文件的结构完整性和数据类型正确性。
|
||||
|
||||
#### Scenario: 检测缺少必需字段
|
||||
|
||||
- **WHEN** YAML 文件缺少 slides 字段
|
||||
- **THEN** 系统报告 ERROR 级别错误:"缺少必需字段 'slides'"
|
||||
|
||||
#### Scenario: 检测数据类型错误
|
||||
|
||||
- **WHEN** slides 字段不是列表类型
|
||||
- **THEN** 系统报告 ERROR 级别错误:"'slides' 必须是一个列表"
|
||||
|
||||
#### Scenario: 检测元素类型无效
|
||||
|
||||
- **WHEN** 元素的 type 字段值不是 text/image/shape/table 之一
|
||||
- **THEN** 系统报告 ERROR 级别错误:"不支持的元素类型: {type}"
|
||||
|
||||
#### Scenario: 检测无效的枚举值
|
||||
|
||||
- **WHEN** shape 元素的 shape 字段值不是 rectangle/ellipse/rounded_rectangle 之一
|
||||
- **THEN** 系统报告 ERROR 级别错误:"不支持的形状类型: {shape}"
|
||||
|
||||
### Requirement: 几何验证
|
||||
|
||||
系统必须验证元素的位置和尺寸是否在页面范围内。
|
||||
|
||||
#### Scenario: 检测元素超出页面右边界
|
||||
|
||||
- **WHEN** 元素的 left + width 超出页面宽度 0.1 英寸以上
|
||||
- **THEN** 系统报告 WARNING 级别警告:"元素右边界超出: {right} > {width}"
|
||||
|
||||
#### Scenario: 检测元素超出页面下边界
|
||||
|
||||
- **WHEN** 元素的 top + height 超出页面高度 0.1 英寸以上
|
||||
- **THEN** 系统报告 WARNING 级别警告:"元素下边界超出: {bottom} > {height}"
|
||||
|
||||
#### Scenario: 容忍计算精度误差
|
||||
|
||||
- **WHEN** 元素的边界超出页面范围不超过 0.1 英寸
|
||||
- **THEN** 系统不报告警告
|
||||
|
||||
#### Scenario: 检测元素完全在页面外
|
||||
|
||||
- **WHEN** 元素的 left >= 页面宽度 或 top >= 页面高度
|
||||
- **THEN** 系统报告 WARNING 级别警告:"元素完全在页面外"
|
||||
|
||||
#### Scenario: 检测表格超出页面范围
|
||||
|
||||
- **WHEN** 表格的 position[0] + sum(col_widths) 超出页面宽度 0.1 英寸以上
|
||||
- **THEN** 系统报告 WARNING 级别警告:"表格超出页面宽度"
|
||||
|
||||
### Requirement: 资源验证
|
||||
|
||||
系统必须验证 YAML 文件引用的外部资源是否存在。
|
||||
|
||||
#### Scenario: 检测图片文件不存在
|
||||
|
||||
- **WHEN** image 元素的 src 指向的文件不存在
|
||||
- **THEN** 系统报告 ERROR 级别错误:"图片文件不存在: {src}"
|
||||
|
||||
#### Scenario: 支持相对路径图片
|
||||
|
||||
- **WHEN** image 元素的 src 是相对路径
|
||||
- **THEN** 系统相对于 YAML 文件所在目录解析路径
|
||||
|
||||
#### Scenario: 检测模板文件不存在
|
||||
|
||||
- **WHEN** 幻灯片引用的 template 文件不存在
|
||||
- **THEN** 系统报告 ERROR 级别错误:"模板文件不存在: {template}"
|
||||
|
||||
#### Scenario: 验证模板文件结构
|
||||
|
||||
- **WHEN** 模板文件存在但结构不正确(缺少 elements 字段)
|
||||
- **THEN** 系统报告 ERROR 级别错误:"模板文件结构错误: {template}"
|
||||
|
||||
### Requirement: 样式验证
|
||||
|
||||
系统必须验证样式相关属性的有效性和合理性。
|
||||
|
||||
#### Scenario: 检测无效的颜色格式
|
||||
|
||||
- **WHEN** 颜色值不符合 #RGB 或 #RRGGBB 格式
|
||||
- **THEN** 系统报告 ERROR 级别错误:"无效的颜色格式: {color}"
|
||||
|
||||
#### Scenario: 检测字体过小
|
||||
|
||||
- **WHEN** 字体大小小于 8pt
|
||||
- **THEN** 系统报告 WARNING 级别警告:"字体太小: {size}pt (建议 >= 8pt)"
|
||||
|
||||
#### Scenario: 检测字体过大
|
||||
|
||||
- **WHEN** 字体大小大于 100pt
|
||||
- **THEN** 系统报告 WARNING 级别警告:"字体太大: {size}pt (建议 <= 100pt)"
|
||||
|
||||
### Requirement: 分级错误报告
|
||||
|
||||
系统必须按照严重程度对验证问题进行分级报告。
|
||||
|
||||
#### Scenario: ERROR 级别阻止转换
|
||||
|
||||
- **WHEN** 验证发现 ERROR 级别问题
|
||||
- **THEN** 系统在独立验证模式下返回非零退出码,在自动验证模式下阻止转换
|
||||
|
||||
#### Scenario: WARNING 级别不阻止转换
|
||||
|
||||
- **WHEN** 验证仅发现 WARNING 级别问题
|
||||
- **THEN** 系统输出警告信息但允许转换继续,返回零退出码
|
||||
|
||||
#### Scenario: 显示问题位置
|
||||
|
||||
- **WHEN** 验证发现问题
|
||||
- **THEN** 系统输出包含幻灯片编号和元素编号的位置信息:"[幻灯片 {n}, 元素 {m}]"
|
||||
|
||||
#### Scenario: 分类统计问题数量
|
||||
|
||||
- **WHEN** 验证完成
|
||||
- **THEN** 系统输出问题统计:"发现 {n} 个错误, {m} 个警告"
|
||||
|
||||
### Requirement: 清晰的命令行输出
|
||||
|
||||
系统必须以清晰易读的格式输出验证结果。
|
||||
|
||||
#### Scenario: 使用图标区分级别
|
||||
|
||||
- **WHEN** 输出验证结果
|
||||
- **THEN** 系统使用 "❌" 表示 ERROR,"⚠️" 表示 WARNING,"ℹ️" 表示 INFO
|
||||
|
||||
#### Scenario: 按级别分组显示
|
||||
|
||||
- **WHEN** 输出验证结果
|
||||
- **THEN** 系统按 ERROR、WARNING、INFO 分组显示问题
|
||||
|
||||
#### Scenario: 验证通过的提示
|
||||
|
||||
- **WHEN** 验证未发现任何问题
|
||||
- **THEN** 系统输出:"✅ 验证通过,未发现问题"
|
||||
Reference in New Issue
Block a user