截图识别飞书个人任务清单
This commit is contained in:
200
utils/config_loader.py
Normal file
200
utils/config_loader.py
Normal file
@@ -0,0 +1,200 @@
|
||||
"""
|
||||
配置文件加载和验证工具
|
||||
"""
|
||||
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any
|
||||
|
||||
|
||||
def load_config(config_path: str) -> Dict[str, Any]:
|
||||
"""
|
||||
加载配置文件
|
||||
|
||||
Args:
|
||||
config_path: 配置文件路径
|
||||
|
||||
Returns:
|
||||
配置字典
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: 配置文件不存在
|
||||
yaml.YAMLError: 配置文件格式错误
|
||||
"""
|
||||
config_file = Path(config_path)
|
||||
|
||||
if not config_file.exists():
|
||||
raise FileNotFoundError(f"配置文件不存在: {config_path}")
|
||||
|
||||
try:
|
||||
with open(config_file, 'r', encoding='utf-8') as f:
|
||||
config = yaml.safe_load(f)
|
||||
return config
|
||||
except yaml.YAMLError as e:
|
||||
raise yaml.YAMLError(f"配置文件格式错误: {str(e)}")
|
||||
|
||||
|
||||
def validate_config(config: Dict[str, Any]) -> None:
|
||||
"""
|
||||
验证配置文件的完整性和有效性
|
||||
|
||||
Args:
|
||||
config: 配置字典
|
||||
|
||||
Raises:
|
||||
ValueError: 配置验证失败
|
||||
"""
|
||||
# 检查必需的顶级配置块
|
||||
required_sections = ['ai', 'feishu', 'system']
|
||||
for section in required_sections:
|
||||
if section not in config:
|
||||
raise ValueError(f"缺少必需的配置块: {section}")
|
||||
|
||||
# 验证AI配置
|
||||
ai_config = config['ai']
|
||||
if not ai_config.get('api_key'):
|
||||
raise ValueError("AI配置缺少 api_key")
|
||||
if not ai_config.get('base_url'):
|
||||
raise ValueError("AI配置缺少 base_url")
|
||||
if not ai_config.get('model'):
|
||||
raise ValueError("AI配置缺少 model")
|
||||
|
||||
# 验证飞书配置
|
||||
feishu_config = config['feishu']
|
||||
if not feishu_config.get('app_id'):
|
||||
raise ValueError("飞书配置缺少 app_id")
|
||||
if not feishu_config.get('app_secret'):
|
||||
raise ValueError("飞书配置缺少 app_secret")
|
||||
if not feishu_config.get('app_token'):
|
||||
raise ValueError("飞书配置缺少 app_token")
|
||||
if not feishu_config.get('table_id'):
|
||||
raise ValueError("飞书配置缺少 table_id")
|
||||
|
||||
# 验证系统配置
|
||||
system_config = config['system']
|
||||
if not system_config.get('watch_folder'):
|
||||
raise ValueError("系统配置缺少 watch_folder")
|
||||
|
||||
# 验证后处理配置
|
||||
post_process = system_config.get('post_process', 'keep')
|
||||
valid_post_process = ['delete', 'move', 'keep']
|
||||
if post_process not in valid_post_process:
|
||||
raise ValueError(f"post_process 必须是 {valid_post_process} 之一,当前值: {post_process}")
|
||||
|
||||
if post_process == 'move' and not system_config.get('processed_folder'):
|
||||
raise ValueError("当 post_process 为 'move' 时,必须配置 processed_folder")
|
||||
|
||||
# 验证路径格式
|
||||
watch_folder = Path(system_config['watch_folder'])
|
||||
if not watch_folder.is_absolute():
|
||||
# 如果是相对路径,转换为绝对路径
|
||||
watch_folder = Path.cwd() / watch_folder
|
||||
|
||||
# 检查监控文件夹是否存在(允许不存在,会在运行时创建)
|
||||
if watch_folder.exists() and not watch_folder.is_dir():
|
||||
raise ValueError(f"watch_folder 必须是一个目录: {watch_folder}")
|
||||
|
||||
# 如果配置了移动目标文件夹,检查其路径
|
||||
if post_process == 'move':
|
||||
processed_folder = Path(system_config['processed_folder'])
|
||||
if not processed_folder.is_absolute():
|
||||
processed_folder = Path.cwd() / processed_folder
|
||||
|
||||
if processed_folder.exists() and not processed_folder.is_dir():
|
||||
raise ValueError(f"processed_folder 必须是一个目录: {processed_folder}")
|
||||
|
||||
|
||||
def save_config(config: Dict[str, Any], config_path: str) -> None:
|
||||
"""
|
||||
保存配置文件
|
||||
|
||||
Args:
|
||||
config: 配置字典
|
||||
config_path: 配置文件路径
|
||||
"""
|
||||
config_file = Path(config_path)
|
||||
|
||||
# 确保目录存在
|
||||
config_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
yaml.dump(config, f, default_flow_style=False, allow_unicode=True)
|
||||
|
||||
|
||||
def create_default_config() -> Dict[str, Any]:
|
||||
"""
|
||||
创建默认配置
|
||||
|
||||
Returns:
|
||||
默认配置字典
|
||||
"""
|
||||
return {
|
||||
'ai': {
|
||||
'api_key': 'sk-xxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
'base_url': 'https://api.siliconflow.cn/v1',
|
||||
'model': 'gemini-2.5-pro'
|
||||
},
|
||||
'feishu': {
|
||||
'app_id': 'cli_xxxxxxxxxxxx',
|
||||
'app_secret': 'xxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
'app_token': 'bascnxxxxxxxxxxxxxxx',
|
||||
'table_id': 'tblxxxxxxxxxxxx'
|
||||
},
|
||||
'system': {
|
||||
'watch_folder': './monitor_images',
|
||||
'post_process': 'move',
|
||||
'processed_folder': './processed_images'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def create_sample_config(config_path: str = 'config.example.yaml') -> None:
|
||||
"""
|
||||
创建示例配置文件
|
||||
|
||||
Args:
|
||||
config_path: 示例配置文件路径
|
||||
"""
|
||||
default_config = create_default_config()
|
||||
save_config(default_config, config_path)
|
||||
print(f"示例配置文件已创建: {config_path}")
|
||||
|
||||
|
||||
def get_config_summary(config: Dict[str, Any]) -> str:
|
||||
"""
|
||||
获取配置摘要信息
|
||||
|
||||
Args:
|
||||
config: 配置字典
|
||||
|
||||
Returns:
|
||||
配置摘要字符串
|
||||
"""
|
||||
summary = []
|
||||
summary.append("配置摘要:")
|
||||
summary.append(f" AI模型: {config['ai']['model']}")
|
||||
summary.append(f" AI服务: {config['ai']['base_url']}")
|
||||
summary.append(f" 监控文件夹: {config['system']['watch_folder']}")
|
||||
summary.append(f" 后处理方式: {config['system'].get('post_process', 'keep')}")
|
||||
|
||||
if config['system'].get('post_process') == 'move':
|
||||
summary.append(f" 处理后文件夹: {config['system']['processed_folder']}")
|
||||
|
||||
return '\n'.join(summary)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 测试配置验证
|
||||
try:
|
||||
# 创建示例配置
|
||||
create_sample_config('config.example.yaml')
|
||||
print("示例配置创建成功!")
|
||||
|
||||
# 加载并验证示例配置
|
||||
config = load_config('config.example.yaml')
|
||||
validate_config(config)
|
||||
print("配置验证通过!")
|
||||
print(get_config_summary(config))
|
||||
|
||||
except Exception as e:
|
||||
print(f"配置测试失败: {e}")
|
||||
Reference in New Issue
Block a user