airlabs-manage/backend/seed_demo.py
seaislee1209 dc42306c24
All checks were successful
Build and Deploy Backend / build-and-deploy (push) Successful in 1m22s
Build and Deploy Web / build-and-deploy (push) Successful in 55s
feat: V2.1 三阶段损耗前端增强 + 路由修复 + 演示数据重写
- 仪表盘双色进度条(超100%蓝红分段)、工时损耗展示、chart tooltip增强
- 修复 Submissions.vue 延期原因字段始终显示的Bug
- 修复 SPA catch-all 路由拦截 API 请求(去尾部斜杠)
- seed_demo.py 重写:5项目/4类型/32里程碑/124提交,真实时间线
- 三阶段损耗计算(前期工时/制作秒数/后期工时)
- ContentType 扩展为11种,里程碑增强(预估天数/开始日期/超期检测)
- 更新 PRD 和项目总结文档

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 18:32:07 +08:00

591 lines
31 KiB
Python
Raw 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.

"""补充演示数据 - 只添加项目/提交/成本,不动用户和角色
展示场景:
1. 双色进度条(品牌方 TVC 进度>200%
2. 工时损耗(分镜/剪辑里程碑超期)
3. 秒数损耗(测试提交 + 超产)
4. 四种项目类型 + 五个项目
5. 前期必须结束才有制作提交
6. 内部原创配有风格测试项目
7. owners / 制片 不提交
"""
from datetime import date, datetime, timedelta
from database import SessionLocal
from models import *
db = SessionLocal()
def get_user(username):
u = db.query(User).filter(User.username == username).first()
if not u:
print(f" [WARN] user '{username}' not found")
return u
def dt(d):
"""date → datetime用于 completed_at"""
return datetime.combine(d, datetime.min.time())
def seed_demo():
# 清除旧的项目相关数据(不动 users 和 roles
db.query(SubmissionHistory).delete()
db.query(Submission).delete()
db.query(OutsourceCost).delete()
db.query(CostOverride).delete()
db.query(AIToolCost).delete()
db.query(OverheadCost).delete()
db.query(ProjectMilestone).delete()
db.query(Project).delete()
db.commit()
print("[1] Cleared old project data")
# ── 获取真实用户 ──
# owners / 制片:不提交任何东西
huhaonan = get_user("huhaonan") # owner/总导演
dengqingrui = get_user("dengqingrui") # owner/AI导演
qiushaohui = get_user("qiushaohui") # 制片
# 组长:可以提交
chenbaodan = get_user("chenbaodan") # 组长/动画制作
maruoqing = get_user("maruoqing") # 组长/AI导演
weichunli = get_user("weichunli") # 组长/AI导演
panziyan = get_user("panziyan") # 组长/剪辑
# 组员
daixiaoqian = get_user("daixiaoqian") # 动画制作
tanruping = get_user("tanruping") # 动画制作
zhengyiqing = get_user("zhengyiqing") # 动画制作
huangxuewen = get_user("huangxuewen") # 动画制作
liushiqi = get_user("liushiqi") # 动画制作
daiwei = get_user("daiwei") # 动画制作
huangrongying = get_user("huangrongying") # 编剧
jiahaozheng = get_user("jiahaozheng") # 剪辑
wangyansen = get_user("wangyansen") # 剪辑
huangqiuxia = get_user("huangqiuxia") # 动画制作
lijing = get_user("lijing") # 动画制作
yemeilian = get_user("yemeilian") # 动画制作
chenxuanying = get_user("chenxuanying") # 动画制作
today = date.today()
# ══════════════════════════════════════════════════════════════
# 项目 1: 品牌方 TVC 宣传片
# 客户正式 | 后期 | target=180s | 创建于 12/8
# 进度>200% → 双色进度条
# 分镜+剪辑超期 → waste_hours
# ══════════════════════════════════════════════════════════════
proj_tvc = Project(
name="品牌方 TVC 宣传片", project_type=ProjectType.CLIENT_FORMAL,
leader_id=maruoqing.id, current_phase=PhaseGroup.POST,
episode_duration_minutes=1, episode_count=3,
estimated_completion_date=date(2026, 2, 20),
contract_amount=50000,
created_at=dt(date(2025, 12, 8)),
)
# ══════════════════════════════════════════════════════════════
# 项目 2: 星际漫游 第一季
# 客户正式 | 制作中 | target=3900s | 创建于 12/22
# 进度~80% | 分镜微超期 → 少量 waste_hours
# ══════════════════════════════════════════════════════════════
proj_star = Project(
name="星际漫游 第一季", project_type=ProjectType.CLIENT_FORMAL,
leader_id=chenbaodan.id, current_phase=PhaseGroup.PRODUCTION,
episode_duration_minutes=5, episode_count=13,
estimated_completion_date=date(2026, 3, 30),
contract_amount=100000,
created_at=dt(date(2025, 12, 22)),
)
# ══════════════════════════════════════════════════════════════
# 项目 3: AI 短剧原创 S1
# 内部原创 | 制作中 | target=2880s | 创建于 1/6
# 进度~40% | 无超期 → waste_hours=0
# ══════════════════════════════════════════════════════════════
proj_orig = Project(
name="AI 短剧原创 S1", project_type=ProjectType.INTERNAL_ORIGINAL,
leader_id=weichunli.id, current_phase=PhaseGroup.PRODUCTION,
episode_duration_minutes=8, episode_count=6,
estimated_completion_date=date(2026, 4, 30),
created_at=dt(date(2026, 1, 6)),
)
# ══════════════════════════════════════════════════════════════
# 项目 4: 原创 S1 风格测试
# 内部测试 | 制作 | target=60s | 创建于 1/2
# 内部原创的配套测试项目 | 全 TEST → 高损耗率
# ══════════════════════════════════════════════════════════════
proj_orig_test = Project(
name="原创 S1 风格测试", project_type=ProjectType.INTERNAL_TEST,
leader_id=weichunli.id, current_phase=PhaseGroup.PRODUCTION,
episode_duration_minutes=1, episode_count=1,
created_at=dt(date(2026, 1, 2)),
)
# ══════════════════════════════════════════════════════════════
# 项目 5: 甲方风格测试
# 客户测试 | 前期 | target=60s | 创建于 1/27
# 早期阶段 | 策划案超期 → waste_hours
# ══════════════════════════════════════════════════════════════
proj_client_test = Project(
name="甲方风格测试", project_type=ProjectType.CLIENT_TEST,
leader_id=maruoqing.id, current_phase=PhaseGroup.PRE,
episode_duration_minutes=1, episode_count=1,
created_at=dt(date(2026, 1, 27)),
)
db.add_all([proj_tvc, proj_star, proj_orig, proj_orig_test, proj_client_test])
db.flush()
print("[2] Created 5 projects")
# ══════════════════════════════════════════════════════════════
# 里程碑
# ══════════════════════════════════════════════════════════════
milestones = []
# ── 品牌方 TVC 里程碑 ──
# 前期
milestones.append(ProjectMilestone(
project_id=proj_tvc.id, name="策划案", phase=PhaseGroup.PRE, sort_order=1,
estimated_days=2, start_date=date(2025, 12, 8),
is_completed=1, completed_at=dt(date(2025, 12, 9)),
))
milestones.append(ProjectMilestone(
project_id=proj_tvc.id, name="分镜", phase=PhaseGroup.PRE, sort_order=3,
estimated_days=2, start_date=date(2025, 12, 10),
is_completed=1, completed_at=dt(date(2025, 12, 15)), # 实际3天,超1天→8h
))
milestones.append(ProjectMilestone(
project_id=proj_tvc.id, name="人设图", phase=PhaseGroup.PRE, sort_order=4,
estimated_days=2, start_date=date(2025, 12, 15),
is_completed=1, completed_at=dt(date(2025, 12, 16)),
))
milestones.append(ProjectMilestone(
project_id=proj_tvc.id, name="场景图", phase=PhaseGroup.PRE, sort_order=5,
estimated_days=1, start_date=date(2025, 12, 17),
is_completed=1, completed_at=dt(date(2025, 12, 17)),
))
# 后期
milestones.append(ProjectMilestone(
project_id=proj_tvc.id, name="配音", phase=PhaseGroup.POST, sort_order=1,
estimated_days=3, start_date=date(2026, 1, 27),
is_completed=1, completed_at=dt(date(2026, 1, 29)),
))
milestones.append(ProjectMilestone(
project_id=proj_tvc.id, name="音效", phase=PhaseGroup.POST, sort_order=2,
estimated_days=2, start_date=date(2026, 1, 29),
is_completed=1, completed_at=dt(date(2026, 1, 30)),
))
milestones.append(ProjectMilestone(
project_id=proj_tvc.id, name="修补镜头", phase=PhaseGroup.POST, sort_order=3,
estimated_days=2, start_date=date(2026, 1, 27),
is_completed=1, completed_at=dt(date(2026, 1, 28)),
))
milestones.append(ProjectMilestone(
project_id=proj_tvc.id, name="剪辑", phase=PhaseGroup.POST, sort_order=4,
estimated_days=3, start_date=date(2026, 2, 3),
is_completed=0, # 进行中,多轮反馈导致超期 → waste_hours
))
# ── 星际漫游 里程碑 ──
# 前期
milestones.append(ProjectMilestone(
project_id=proj_star.id, name="策划案", phase=PhaseGroup.PRE, sort_order=1,
estimated_days=3, start_date=date(2025, 12, 22),
is_completed=1, completed_at=dt(date(2025, 12, 23)),
))
milestones.append(ProjectMilestone(
project_id=proj_star.id, name="剧本", phase=PhaseGroup.PRE, sort_order=2,
estimated_days=5, start_date=date(2025, 12, 24),
is_completed=1, completed_at=dt(date(2025, 12, 30)),
))
milestones.append(ProjectMilestone(
project_id=proj_star.id, name="分镜", phase=PhaseGroup.PRE, sort_order=3,
estimated_days=3, start_date=date(2025, 12, 31),
is_completed=1, completed_at=dt(date(2026, 1, 7)), # 实际5天,超2天→16h
))
milestones.append(ProjectMilestone(
project_id=proj_star.id, name="人设图", phase=PhaseGroup.PRE, sort_order=4,
estimated_days=2, start_date=date(2026, 1, 8),
is_completed=1, completed_at=dt(date(2026, 1, 9)),
))
milestones.append(ProjectMilestone(
project_id=proj_star.id, name="场景图", phase=PhaseGroup.PRE, sort_order=5,
estimated_days=2, start_date=date(2026, 1, 10),
is_completed=1, completed_at=dt(date(2026, 1, 13)),
))
# 后期(未开始)
for name, order in [("配音", 1), ("音效", 2), ("修补镜头", 3), ("剪辑", 4)]:
milestones.append(ProjectMilestone(
project_id=proj_star.id, name=name, phase=PhaseGroup.POST, sort_order=order,
))
# ── AI 短剧原创 里程碑 ──(全部按时)
milestones.append(ProjectMilestone(
project_id=proj_orig.id, name="策划案", phase=PhaseGroup.PRE, sort_order=1,
estimated_days=2, start_date=date(2026, 1, 6),
is_completed=1, completed_at=dt(date(2026, 1, 7)),
))
milestones.append(ProjectMilestone(
project_id=proj_orig.id, name="剧本", phase=PhaseGroup.PRE, sort_order=2,
estimated_days=5, start_date=date(2026, 1, 8),
is_completed=1, completed_at=dt(date(2026, 1, 14)),
))
milestones.append(ProjectMilestone(
project_id=proj_orig.id, name="分镜", phase=PhaseGroup.PRE, sort_order=3,
estimated_days=3, start_date=date(2026, 1, 15),
is_completed=1, completed_at=dt(date(2026, 1, 17)),
))
milestones.append(ProjectMilestone(
project_id=proj_orig.id, name="人设图", phase=PhaseGroup.PRE, sort_order=4,
estimated_days=2, start_date=date(2026, 1, 20),
is_completed=1, completed_at=dt(date(2026, 1, 21)),
))
milestones.append(ProjectMilestone(
project_id=proj_orig.id, name="场景图", phase=PhaseGroup.PRE, sort_order=5,
estimated_days=1, start_date=date(2026, 1, 22),
is_completed=1, completed_at=dt(date(2026, 1, 22)),
))
for name, order in [("配音", 1), ("音效", 2), ("修补镜头", 3), ("剪辑", 4)]:
milestones.append(ProjectMilestone(
project_id=proj_orig.id, name=name, phase=PhaseGroup.POST, sort_order=order,
))
# ── 原创风格测试 里程碑 ──
milestones.append(ProjectMilestone(
project_id=proj_orig_test.id, name="策划案", phase=PhaseGroup.PRE, sort_order=1,
estimated_days=2, start_date=date(2026, 1, 2),
is_completed=1, completed_at=dt(date(2026, 1, 3)),
))
milestones.append(ProjectMilestone(
project_id=proj_orig_test.id, name="人设图", phase=PhaseGroup.PRE, sort_order=4,
estimated_days=2, start_date=date(2026, 1, 6),
is_completed=1, completed_at=dt(date(2026, 1, 7)),
))
milestones.append(ProjectMilestone(
project_id=proj_orig_test.id, name="剪辑", phase=PhaseGroup.POST, sort_order=4,
))
# ── 甲方风格测试 里程碑 ──
milestones.append(ProjectMilestone(
project_id=proj_client_test.id, name="策划案", phase=PhaseGroup.PRE, sort_order=1,
estimated_days=3, start_date=date(2026, 1, 27),
is_completed=0, # 甲方反复修改,至今未定稿 → 超期 → waste_hours
))
milestones.append(ProjectMilestone(
project_id=proj_client_test.id, name="人设图", phase=PhaseGroup.PRE, sort_order=4,
estimated_days=2, start_date=date(2026, 2, 3),
is_completed=0,
))
milestones.append(ProjectMilestone(
project_id=proj_client_test.id, name="剪辑", phase=PhaseGroup.POST, sort_order=4,
))
db.add_all(milestones)
db.flush()
print(f"[2.5] Created {len(milestones)} milestones")
# ══════════════════════════════════════════════════════════════
# 内容提交
# ══════════════════════════════════════════════════════════════
subs = []
def add(user, proj, phase, wt, ct, secs, d, desc, hours=None):
"""快捷添加提交"""
subs.append(Submission(
user_id=user.id, project_id=proj.id,
project_phase=phase, work_type=wt,
content_type=ct, total_seconds=secs,
duration_minutes=secs // 60 if secs else 0,
duration_seconds=secs % 60 if secs else 0,
hours_spent=hours, submit_to=SubmitTo.INTERNAL,
description=desc, submit_date=d,
))
PRE = PhaseGroup.PRE
PROD = PhaseGroup.PRODUCTION
POST = PhaseGroup.POST
PLAN = WorkType.PLAN
MFG = WorkType.PRODUCTION
TEST = WorkType.TEST
# ────────────────────────────────────────────
# 品牌方 TVC 宣传片 (target=180s, 创建 12/8)
# 前期: 12/8 - 12/18
# 制作: 12/22 - 1/10
# 后期: 1/27 - now
# ────────────────────────────────────────────
# 前期 - 策划案 (12/8-9)
add(huangrongying, proj_tvc, PRE, PLAN, ContentType.PLANNING, 0, date(2025, 12, 8), "TVC 策划案初稿", 4)
add(huangrongying, proj_tvc, PRE, PLAN, ContentType.PLANNING, 0, date(2025, 12, 9), "TVC 策划案定稿", 3)
# 前期 - 分镜 (12/10-15, 比预期多1天)
add(daixiaoqian, proj_tvc, PRE, PLAN, ContentType.STORYBOARD, 0, date(2025, 12, 10), "TVC 分镜初稿", 5)
add(daixiaoqian, proj_tvc, PRE, PLAN, ContentType.STORYBOARD, 0, date(2025, 12, 11), "TVC 分镜修改", 4)
add(daixiaoqian, proj_tvc, PRE, PLAN, ContentType.STORYBOARD, 0, date(2025, 12, 15), "TVC 分镜终稿", 3)
# 前期 - 人设图 (12/15-16)
add(huangxuewen, proj_tvc, PRE, PLAN, ContentType.CHARACTER_DESIGN, 0, date(2025, 12, 15), "TVC 人设图初稿", 5)
add(huangxuewen, proj_tvc, PRE, PLAN, ContentType.CHARACTER_DESIGN, 0, date(2025, 12, 16), "TVC 人设图定稿", 3)
# 前期 - 场景图 (12/17)
add(huangxuewen, proj_tvc, PRE, PLAN, ContentType.SCENE_DESIGN, 0, date(2025, 12, 17), "TVC 场景图", 4)
# 制作 - 马若情 (12/22 - 12/26 第一轮, 1/8-9 修改轮)
add(maruoqing, proj_tvc, PROD, TEST, ContentType.ANIMATION, 35, date(2025, 12, 22), "TVC 片段测试", 3)
add(maruoqing, proj_tvc, PROD, MFG, ContentType.ANIMATION, 70, date(2025, 12, 23), "TVC 第1集场景1", 4)
add(maruoqing, proj_tvc, PROD, MFG, ContentType.ANIMATION, 65, date(2025, 12, 24), "TVC 第1集场景2", 4)
add(maruoqing, proj_tvc, PROD, MFG, ContentType.ANIMATION, 75, date(2025, 12, 25), "TVC 第2集场景1", 4)
add(maruoqing, proj_tvc, PROD, MFG, ContentType.ANIMATION, 60, date(2025, 12, 26), "TVC 第2-3集", 4)
# 甲方反馈后修改
add(maruoqing, proj_tvc, PROD, MFG, ContentType.ANIMATION, 55, date(2026, 1, 8), "TVC 修改-第1集重做", 3)
add(maruoqing, proj_tvc, PROD, MFG, ContentType.ANIMATION, 50, date(2026, 1, 9), "TVC 修改-第3集调整", 3)
# 后期 - 修补镜头 (秒数,归入制作计算) 1/27-28
add(liushiqi, proj_tvc, POST, MFG, ContentType.SHOT_REPAIR, 25, date(2026, 1, 27), "TVC 修补镜头1", 1.5)
add(liushiqi, proj_tvc, POST, MFG, ContentType.SHOT_REPAIR, 30, date(2026, 1, 28), "TVC 修补镜头2", 1.5)
# 后期 - 配音 (1/27-29, 每天1集)
add(panziyan, proj_tvc, POST, MFG, ContentType.DUBBING, 0, date(2026, 1, 27), "TVC 第1集配音", 4)
add(panziyan, proj_tvc, POST, MFG, ContentType.DUBBING, 0, date(2026, 1, 28), "TVC 第2集配音", 4)
add(panziyan, proj_tvc, POST, MFG, ContentType.DUBBING, 0, date(2026, 1, 29), "TVC 第3集配音", 3)
# 后期 - 音效 (1/29-30)
add(panziyan, proj_tvc, POST, MFG, ContentType.SOUND_EFFECTS, 0, date(2026, 1, 29), "TVC 音效设计", 5)
add(panziyan, proj_tvc, POST, MFG, ContentType.SOUND_EFFECTS, 0, date(2026, 1, 30), "TVC 音效终混", 3)
# 后期 - 剪辑 (2/3-now, 多轮甲方反馈导致超期)
add(wangyansen, proj_tvc, POST, MFG, ContentType.EDITING, 0, date(2026, 2, 3), "TVC 第1-2集粗剪", 6)
add(wangyansen, proj_tvc, POST, MFG, ContentType.EDITING, 0, date(2026, 2, 4), "TVC 第3集粗剪+精剪", 5)
add(wangyansen, proj_tvc, POST, MFG, ContentType.EDITING, 0, date(2026, 2, 10), "TVC 甲方反馈后重剪", 5)
add(wangyansen, proj_tvc, POST, MFG, ContentType.EDITING, 0, date(2026, 2, 12), "TVC 第二轮反馈修改", 4)
# ────────────────────────────────────────────
# 星际漫游 第一季 (target=3900s, 创建 12/22)
# 前期: 12/22 - 1/13
# 制作: 1/14 - now
# ────────────────────────────────────────────
# 前期 - 策划案 (12/22-23)
add(huangrongying, proj_star, PRE, PLAN, ContentType.PLANNING, 0, date(2025, 12, 22), "星际漫游 世界观策划案", 4)
add(huangrongying, proj_star, PRE, PLAN, ContentType.PLANNING, 0, date(2025, 12, 23), "星际漫游 策划案终稿", 3)
# 前期 - 剧本 (12/24-30, 5个工作日)
for i, d in enumerate([date(2025, 12, 24), date(2025, 12, 25), date(2025, 12, 26),
date(2025, 12, 29), date(2025, 12, 30)]):
add(huangrongying, proj_star, PRE, PLAN, ContentType.SCRIPT, 0, d,
f"{i*3+1}-{i*3+3}集剧本", 6)
# 前期 - 分镜 (12/31-1/7, 比预期多, 导致超期 → 16h waste)
for i, d in enumerate([date(2025, 12, 31), date(2026, 1, 2), date(2026, 1, 5),
date(2026, 1, 6), date(2026, 1, 7)]):
add(daixiaoqian, proj_star, PRE, PLAN, ContentType.STORYBOARD, 0, d,
f"{i+1}批分镜({i*3+1}-{i*3+3}集)", 5)
# 前期 - 人设图 (1/8-9)
add(huangxuewen, proj_star, PRE, PLAN, ContentType.CHARACTER_DESIGN, 0, date(2026, 1, 8), "主要角色人设图", 5)
add(huangxuewen, proj_star, PRE, PLAN, ContentType.CHARACTER_DESIGN, 0, date(2026, 1, 9), "配角人设图", 4)
# 前期 - 场景图 (1/10-13)
add(huangxuewen, proj_star, PRE, PLAN, ContentType.SCENE_DESIGN, 0, date(2026, 1, 12), "太空站场景图", 5)
add(huangxuewen, proj_star, PRE, PLAN, ContentType.SCENE_DESIGN, 0, date(2026, 1, 13), "星球表面场景图", 4)
# 制作 - 1/14 起 (~22个工作日到今天)
# 3个动画师,不是每天都在这个项目上
# 陈保丹: 14次提交 (2测试 + 12制作)
star_anim_dates_chen = [
date(2026, 1, 14), date(2026, 1, 15), date(2026, 1, 16), date(2026, 1, 19),
date(2026, 1, 20), date(2026, 1, 22), date(2026, 1, 23), date(2026, 1, 26),
date(2026, 1, 28), date(2026, 1, 30), date(2026, 2, 3), date(2026, 2, 5),
date(2026, 2, 9), date(2026, 2, 11),
]
star_secs_chen = [70, 80, 85, 75, 90, 80, 95, 70, 85, 90, 80, 75, 85, 80]
for i, (d, s) in enumerate(zip(star_anim_dates_chen, star_secs_chen)):
wt = TEST if i < 2 else MFG
add(chenbaodan, proj_star, PROD, wt, ContentType.ANIMATION, s, d,
f"{(i//2)+1}集 场景{(i%4)+1}{'(测试)' if wt == TEST else ''}", 3.5)
# 谭如平: 12次提交 (1测试 + 11制作)
star_anim_dates_tan = [
date(2026, 1, 15), date(2026, 1, 16), date(2026, 1, 19), date(2026, 1, 21),
date(2026, 1, 23), date(2026, 1, 27), date(2026, 1, 29), date(2026, 2, 2),
date(2026, 2, 4), date(2026, 2, 6), date(2026, 2, 10), date(2026, 2, 12),
]
star_secs_tan = [75, 70, 80, 65, 85, 70, 75, 80, 90, 70, 75, 85]
for i, (d, s) in enumerate(zip(star_anim_dates_tan, star_secs_tan)):
wt = TEST if i < 1 else MFG
add(tanruping, proj_star, PROD, wt, ContentType.ANIMATION, s, d,
f"{(i//2)+3}集 镜头{(i%3)+1}{'(测试)' if wt == TEST else ''}", 3)
# 郑奕晴: 11次提交 (1测试 + 10制作)
star_anim_dates_zheng = [
date(2026, 1, 16), date(2026, 1, 20), date(2026, 1, 22), date(2026, 1, 26),
date(2026, 1, 28), date(2026, 2, 2), date(2026, 2, 4), date(2026, 2, 6),
date(2026, 2, 10), date(2026, 2, 12), date(2026, 2, 13),
]
star_secs_zheng = [85, 75, 90, 80, 85, 95, 70, 80, 75, 90, 85]
for i, (d, s) in enumerate(zip(star_anim_dates_zheng, star_secs_zheng)):
wt = TEST if i < 1 else MFG
add(zhengyiqing, proj_star, PROD, wt, ContentType.ANIMATION, s, d,
f"{(i//2)+5}集 动画{(i%3)+1}{'(测试)' if wt == TEST else ''}", 3.5)
# ────────────────────────────────────────────
# AI 短剧原创 S1 (target=2880s, 创建 1/6)
# 前期: 1/6 - 1/22
# 制作: 1/23 - now
# ────────────────────────────────────────────
# 前期 - 策划案 (1/6-7)
add(huangrongying, proj_orig, PRE, PLAN, ContentType.PLANNING, 0, date(2026, 1, 6), "原创S1 世界观策划案", 4)
add(huangrongying, proj_orig, PRE, PLAN, ContentType.PLANNING, 0, date(2026, 1, 7), "原创S1 策划案终稿", 3)
# 前期 - 剧本 (1/8-14)
for i, d in enumerate([date(2026, 1, 8), date(2026, 1, 9), date(2026, 1, 12),
date(2026, 1, 13), date(2026, 1, 14)]):
add(huangrongying, proj_orig, PRE, PLAN, ContentType.SCRIPT, 0, d,
f"原创S1 第{i+1}集剧本", 6)
# 前期 - 分镜 (1/15-17)
for i, d in enumerate([date(2026, 1, 15), date(2026, 1, 16), date(2026, 1, 17)]):
add(daixiaoqian, proj_orig, PRE, PLAN, ContentType.STORYBOARD, 0, d,
f"原创S1 第{i*2+1}-{i*2+2}集分镜", 5)
# 前期 - 人设图 (1/20-21)
add(huangxuewen, proj_orig, PRE, PLAN, ContentType.CHARACTER_DESIGN, 0, date(2026, 1, 20), "原创S1 主角人设图", 5)
add(huangxuewen, proj_orig, PRE, PLAN, ContentType.CHARACTER_DESIGN, 0, date(2026, 1, 21), "原创S1 配角人设图", 4)
# 前期 - 场景图 (1/22)
add(huangxuewen, proj_orig, PRE, PLAN, ContentType.SCENE_DESIGN, 0, date(2026, 1, 22), "原创S1 场景图", 5)
# 制作 - 1/23 起 (~16个工作日到今天)
# 魏春丽: 6次, 黄秋霞: 5次, 李静: 4次
orig_dates_wei = [date(2026, 1, 23), date(2026, 1, 26), date(2026, 1, 29),
date(2026, 2, 3), date(2026, 2, 6), date(2026, 2, 10)]
orig_secs_wei = [80, 90, 75, 85, 95, 80]
for i, (d, s) in enumerate(zip(orig_dates_wei, orig_secs_wei)):
add(weichunli, proj_orig, PROD, MFG, ContentType.ANIMATION, s, d,
f"原创S1 第1集 片段{i+1}", 4)
orig_dates_huang = [date(2026, 1, 27), date(2026, 1, 30), date(2026, 2, 4),
date(2026, 2, 9), date(2026, 2, 12)]
orig_secs_huang = [70, 80, 65, 75, 85]
for i, (d, s) in enumerate(zip(orig_dates_huang, orig_secs_huang)):
add(huangqiuxia, proj_orig, PROD, MFG, ContentType.ANIMATION, s, d,
f"原创S1 第2集 动画{i+1}", 3.5)
orig_dates_li = [date(2026, 2, 3), date(2026, 2, 5), date(2026, 2, 10), date(2026, 2, 13)]
orig_secs_li = [75, 70, 80, 65]
for i, (d, s) in enumerate(zip(orig_dates_li, orig_secs_li)):
add(lijing, proj_orig, PROD, MFG, ContentType.ANIMATION, s, d,
f"原创S1 第3集 片段{i+1}", 3.5)
# ────────────────────────────────────────────
# 原创 S1 风格测试 (target=60s, 创建 1/2)
# 前期: 1/2-7, 制作(全测试): 1/8-17
# ────────────────────────────────────────────
# 前期 - 策划案 (1/2-3)
add(huangrongying, proj_orig_test, PRE, PLAN, ContentType.PLANNING, 0, date(2026, 1, 2), "原创风格测试 策划案", 3)
add(huangrongying, proj_orig_test, PRE, PLAN, ContentType.PLANNING, 0, date(2026, 1, 3), "原创风格测试 策划终稿", 2)
# 前期 - 人设图 (1/6-7)
add(huangxuewen, proj_orig_test, PRE, PLAN, ContentType.CHARACTER_DESIGN, 0, date(2026, 1, 6), "风格测试 人设图初稿", 4)
add(huangxuewen, proj_orig_test, PRE, PLAN, ContentType.CHARACTER_DESIGN, 0, date(2026, 1, 7), "风格测试 人设图定稿", 3)
# 制作(测试) - 戴伟全部 TEST
test_dates = [date(2026, 1, 8), date(2026, 1, 9), date(2026, 1, 12),
date(2026, 1, 14), date(2026, 1, 16)]
test_secs = [12, 14, 10, 16, 18]
test_descs = ["角色动作测试", "场景氛围测试", "打光方案测试", "运镜方案A", "运镜方案B"]
for d, s, desc in zip(test_dates, test_secs, test_descs):
add(daiwei, proj_orig_test, PROD, TEST, ContentType.ANIMATION, s, d,
f"原创风格测试-{desc}", 2)
# ────────────────────────────────────────────
# 甲方风格测试 (target=60s, 创建 1/27)
# 前期: 1/27 - now (策划案仍未定稿,甲方反复修改)
# ────────────────────────────────────────────
# 前期 - 策划案 (1/27起,至今未完成)
add(huangrongying, proj_client_test, PRE, PLAN, ContentType.PLANNING, 0, date(2026, 1, 27), "甲方风格策划案 v1", 4)
add(huangrongying, proj_client_test, PRE, PLAN, ContentType.PLANNING, 0, date(2026, 1, 30), "甲方风格策划案 v2(反馈修改)", 3)
add(huangrongying, proj_client_test, PRE, PLAN, ContentType.PLANNING, 0, date(2026, 2, 5), "甲方风格策划案 v3(二次反馈)", 3)
add(huangrongying, proj_client_test, PRE, PLAN, ContentType.PLANNING, 0, date(2026, 2, 11), "甲方风格策划案 v4(三次反馈)", 3)
# 前期 - 人设图 (2/3-5, 边做边等策划案定稿)
add(huangxuewen, proj_client_test, PRE, PLAN, ContentType.CHARACTER_DESIGN, 0, date(2026, 2, 3), "甲方风格 人设图初稿", 5)
add(huangxuewen, proj_client_test, PRE, PLAN, ContentType.CHARACTER_DESIGN, 0, date(2026, 2, 5), "甲方风格 人设图修改", 4)
# 少量测试动画 (2/10-12, 用于给甲方展示方向)
add(daiwei, proj_client_test, PROD, TEST, ContentType.ANIMATION, 10, date(2026, 2, 10), "甲方风格测试片段1", 2)
add(daiwei, proj_client_test, PROD, TEST, ContentType.ANIMATION, 14, date(2026, 2, 12), "甲方风格测试片段2", 2)
db.add_all(subs)
print(f"[3] Created {len(subs)} submissions")
# ══════════════════════════════════════════════════════════════
# 成本数据
# ══════════════════════════════════════════════════════════════
# AI 工具成本
db.add(AIToolCost(
tool_name="Midjourney", subscription_period=SubscriptionPeriod.MONTHLY,
amount=200, allocation_type=CostAllocationType.TEAM,
recorded_by=qiushaohui.id, record_date=today.replace(day=1),
))
db.add(AIToolCost(
tool_name="Runway", subscription_period=SubscriptionPeriod.MONTHLY,
amount=600, allocation_type=CostAllocationType.PROJECT,
project_id=proj_star.id,
recorded_by=qiushaohui.id, record_date=today.replace(day=1),
))
db.add(AIToolCost(
tool_name="ChatGPT Plus", subscription_period=SubscriptionPeriod.MONTHLY,
amount=150, allocation_type=CostAllocationType.TEAM,
recorded_by=qiushaohui.id, record_date=today.replace(day=1),
))
print("[4] Created 3 AI tool costs")
# 外包成本
db.add(OutsourceCost(
project_id=proj_star.id, outsource_type=OutsourceType.ANIMATION,
episode_start=10, episode_end=13, amount=20000,
recorded_by=qiushaohui.id, record_date=today - timedelta(days=5),
))
print("[5] Created 1 outsource cost")
# 固定开支
db.add(OverheadCost(
cost_type=OverheadCostType.OFFICE_RENT,
amount=8000, record_month=today.strftime("%Y-%m"),
recorded_by=qiushaohui.id, note="办公室月租",
))
db.add(OverheadCost(
cost_type=OverheadCostType.UTILITIES,
amount=500, record_month=today.strftime("%Y-%m"),
recorded_by=qiushaohui.id, note="水电费",
))
print("[6] Created 2 overhead costs")
db.commit()
# ── 打印统计摘要 ──
print(f"\n{'='*55}")
print("[DONE] Demo data seeded successfully!")
print(f" Projects: 5 | Milestones: {len(milestones)}")
print(f" Submissions: {len(subs)}")
print(f" AI tools: 3 | Outsource: 1 | Overhead: 2")
print(f"\n 预期展示效果:")
print(f" · 品牌方 TVC 进度>200% → 蓝红双色进度条")
print(f" · 星际漫游/TVC/甲方测试有超期里程碑 → 工时损耗")
print(f" · 原创风格测试全测试 → 高 waste_rate")
print(f" · 5个项目覆盖4种类型, created_at设为过去日期")
print(f" · 前期结束后才有制作提交")
print(f" · owners/制片无提交")
if __name__ == "__main__":
seed_demo()