skills/ppt-station-skill/ppt_station/models/job.py
wangyitong a65adcc2e5 Initial commit: merged, deduplicated, and vetted skill collection
Sources: extracted from two upstream archives (skill-repo, skills-main),
merged with the following policy:

- 15 broken symlinks (pointing to /Users/jameslee/.cc-switch/skills or
  ../../.agents/skills on a foreign machine) discarded
- 3 real name collisions with identical content (ai-pair, ifind-http-api,
  zhipu-websearch) kept as one copy
- Functional overlaps deduped keeping the strongest variant:
  - docx family: kept docx (official, full toolchain) + docx-cn
    (GB/T 9704 Chinese official-document constants),
    dropped docx_writer (no scripts, name collided with docx)
  - humanizer family: kept humanizer-zh (6 zh reference docs),
    dropped humanizer (en, redundant for CN workflow)
- Skills that only ran in a foreign environment removed:
  ablemind-ops, app-publish, hlb-design-system, openclaw-adj-skill,
  claude-driver
- alphapai excluded from this public repo because its SKILL.md hard-coded
  live credentials

Result: 25 skills, 572 files, ~7.5 MB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 14:47:12 +08:00

177 lines
5.3 KiB
Python
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.

"""
任务配置数据模型
"""
from typing import Dict, List, Any, Optional, Literal
from pydantic import BaseModel, Field, model_validator
class DataSource(BaseModel):
"""数据源配置"""
type: Literal["csv", "xlsx", "sql", "http", "tushare"]
# CSV/Excel 配置
path: Optional[str] = None
encoding: Optional[str] = "utf-8"
sheet_name: Optional[str] = None
# SQL 配置
conn: Optional[str] = None
query: Optional[str] = None
params: Optional[Dict[str, Any]] = None
# HTTP 配置
url: Optional[str] = None
method: Optional[str] = "GET"
headers: Optional[Dict[str, str]] = None
# Tushare 配置
api_name: Optional[str] = None # pro_bar, index_daily, etc.
ts_code: Optional[str] = None # 股票/指数代码
index_code: Optional[str] = None # 指数代码
start_date: Optional[str] = None
end_date: Optional[str] = None
fields: Optional[List[str]] = None
class TransformOp(BaseModel):
"""数据转换操作"""
type: Literal["groupby", "pivot", "merge", "compute", "filter", "sort", "rename"]
# groupby 参数
by: Optional[List[str]] = None
agg: Optional[Dict[str, str]] = None
# pivot 参数
index: Optional[str] = None
columns: Optional[str] = None
values: Optional[str] = None
# merge 参数
on: Optional[str | List[str]] = None
how: Optional[str] = "inner"
# compute 参数
expr: Optional[str] = None
output_col: Optional[str] = None
# filter 参数
condition: Optional[str] = None
# sort 参数
sort_by: Optional[List[str]] = None
ascending: Optional[bool] = True
# rename 参数
map: Optional[Dict[str, str]] = None
class Transform(BaseModel):
"""数据转换配置"""
from_: str | List[str] = Field(..., alias="from")
ops: List[TransformOp]
class ChartSeriesSpec(BaseModel):
"""图表系列配置"""
key: str # DataFrame 列名
name: str # 显示名称
type: Optional[Literal["bar", "line", "area"]] = None # 图表类型composer 模式用)
axis: Optional[Literal["primary", "secondary"]] = None
slot: Optional[int] = None # 对齐模板系列顺序
class ChartSpec(BaseModel):
"""图表配置"""
mode: Literal["create", "update", "excel_embedded"] = "update"
target: Optional[str] = None # 模板中的图表名称update 模式)
target_placeholder: Optional[str] = None # 占位符名称create/excel_embedded 模式)
chart_type: Optional[str] = None # XL_CHART_TYPEcreate 模式)或 Excel 图表类型
source: str # 数据源名称
categories: str # 分类列名
series: List[ChartSeriesSpec]
respect_template_types: bool = True
series_order_strict: bool = True
def has_secondary_axis(self) -> bool:
"""检查是否有次坐标轴系列"""
return any(s.axis == "secondary" for s in self.series)
class TableSpec(BaseModel):
"""表格配置"""
target: str # 模板中的表格名称
source: str # 数据源名称
columns: List[str]
header: Optional[List[str]] = None
number_format: Optional[Dict[str, str]] = None
class TextSpec(BaseModel):
"""文本配置"""
target: str # 模板中的文本框名称
value: str # 支持 Jinja2 模板语法
class SlideSpec(BaseModel):
"""幻灯片配置"""
id: str
layout: Optional[str] = None # 版式名称
texts: Optional[List[TextSpec]] = None
tables: Optional[List[TableSpec]] = None
charts: Optional[List[ChartSpec]] = None
class TemplateSpec(BaseModel):
"""模板配置"""
path: str
master: Optional[str] = None
notes: Optional[str] = None
class OutputSpec(BaseModel):
"""输出配置"""
path: str
overwrite: bool = True
add_metadata: bool = True # 是否添加生成信息到元数据
class ComposerPageSpec(BaseModel):
"""Composer 模式的页面规格"""
layout: str # LAYOUT_REGISTRY 中的布局名称
data: Dict[str, Any] # 传给布局函数的 data dict
class Job(BaseModel):
"""完整的任务配置 — 支持 template 和 composer 两种模式"""
mode: Literal["template", "composer"] = "template"
# template 模式字段
template: Optional[TemplateSpec] = None
slides: Optional[List[SlideSpec]] = None
# composer 模式字段
theme: Optional[str] = None # THEMES 中的名称
aspect_ratio: Optional[str] = None # "4:3" 或 "16:9"(默认 16:9
pages: Optional[List[ComposerPageSpec]] = None
# 共用字段
datasources: Dict[str, DataSource] = Field(default_factory=dict)
transforms: Optional[Dict[str, Transform]] = None
params: Optional[Dict[str, Any]] = None
default_layout_config: Optional[Dict[str, Any]] = None # 图表布局默认配置
output: OutputSpec
class Config:
populate_by_name = True
@model_validator(mode="after")
def check_mode_fields(self):
if self.mode == "template":
if not self.template:
raise ValueError("template 模式必须提供 template 字段")
if not self.slides:
raise ValueError("template 模式必须提供 slides 字段")
elif self.mode == "composer":
if not self.pages:
raise ValueError("composer 模式必须提供 pages 字段")
return self