PMFlow/backend/app/seed_projects.py

385 lines
14 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.

"""
Seed Projects and Signals
初始化示例项目和信号数据(新模型:BigInteger id)
含 SSTC 宪章:Pixelle-Video 项目关联宪章与任务
"""
import asyncio
from datetime import datetime
from typing import Optional
from sqlalchemy import func, select, or_
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import sessionmaker
from app.models import Signal, Project, ProjectSignalLink, TruthNode, Task, User
from app.services.auth import AuthService
PIXELLE_NAME = "Pixelle-Video 集成"
async def seed_pixelle_project(db: Optional[AsyncSession] = None):
"""
创建或恢复 Pixelle-Video 示例项目:无项目则全量创建;有项目但无宪章/无信号链接则补全 SSTC 数据。
"""
from app.database import engine
if db is None:
async_session_maker = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async with async_session_maker() as session:
await _seed_projects(session)
else:
await _seed_projects(db)
async def _ensure_pixelle_sstc_data(session: AsyncSession, pixelle_project: Project) -> bool:
"""
若「Pixelle-Video 集成」已存在但无宪章或无项目-信号链接,则补全信号、链接、宪章与任务。返回是否做了变更。
"""
import json
pid = pixelle_project.id
# 是否已有项目-信号链接
link_count = await session.execute(
select(func.count()).select_from(ProjectSignalLink).where(ProjectSignalLink.project_id == pid)
)
has_links = (link_count.scalar() or 0) > 0
# 是否已有宪章
charter_count = await session.execute(
select(func.count()).select_from(TruthNode).where(
TruthNode.project_id == pid, TruthNode.node_type == "charter"
)
)
has_charters = (charter_count.scalar() or 0) > 0
if has_links and has_charters:
return False
changed = False
pixelle_signals = []
if not has_links:
pixelle_signals = [
Signal(
source="HackerNews",
content="Pixelle-Video: An AI-powered automated short video generation platform with script-to-video automation",
impact_score=9.2,
priority="critical",
processed=True,
),
Signal(
source="ProductHunt",
content="Pixelle-Video launches API for programmatic video content generation",
impact_score=8.5,
priority="high",
processed=True,
),
Signal(
source="TechCrunch",
content="Pixelle-Video raises Series A funding to scale AI video infrastructure",
impact_score=7.8,
priority="high",
processed=True,
),
]
for signal in pixelle_signals:
signal.strategic_analysis = json.dumps({
"decision_proposal": {
"is_strategic": True,
"threat_level": "HIGH",
"lead_time_days": 14,
"impact_score": getattr(signal, "impact_score", 0),
"recommendation": "立即集成 Pixelle-Video API 到主平台"
},
"competitiveness_radar": {
"innovation": 85,
"market_potential": 90,
"technical_complexity": 75,
"resource_intensity": 70
},
"detected_tech_stacks": ["AI/ML", "Video Processing", "REST API"],
"related_knowledge_nodes": [],
"rag_context_used": 0
})
signal.tasks_generated = json.dumps([
{"task_name": "Integrate Pixelle-Video API", "type": "backend", "estimated_hours": 8.0},
{"task_name": "Design Video Workflow UI", "type": "frontend", "estimated_hours": 12.0},
{"task_name": "Optimize Video Pipeline", "type": "ai_engineer", "estimated_hours": 16.0},
])
for signal in pixelle_signals:
session.add(signal)
await session.flush()
for i, signal in enumerate(pixelle_signals):
link = ProjectSignalLink(
project_id=pid,
signal_id=signal.id,
link_type="related",
relevance_score=1.0,
)
session.add(link)
pixelle_project.signal_count = len(pixelle_signals)
changed = True
await session.flush()
if not has_charters:
user_result = await session.execute(select(User).limit(1))
seed_user = user_result.scalar_one_or_none()
if not seed_user:
seed_user = User(
username="boss",
email="boss@pmflow.local",
password_hash=AuthService.hash_password("boss123"),
name="负责人",
role="BOSS",
status="active",
)
session.add(seed_user)
await session.flush()
if pixelle_signals:
signal_ids = [s.id for s in pixelle_signals]
else:
link_rows = await session.execute(
select(ProjectSignalLink.signal_id).where(ProjectSignalLink.project_id == pid)
)
signal_ids = [r[0] for r in link_rows.fetchall()]
charter = TruthNode(
node_type="charter",
parent_id=None,
project_id=pid,
created_by=seed_user.id,
title="Pixelle-Video 集成需求宪章",
content={
"rationale": "基于 HackerNews/ProductHunt/TechCrunch 信号,决定集成 Pixelle-Video API 到主平台",
"signal_ids": signal_ids,
"decision": "立即集成 Pixelle-Video API,支持脚本到视频的自动化生成",
},
version=1,
status="aligned",
)
session.add(charter)
await session.flush()
for idx, task_item in enumerate([
{"title": "Integrate Pixelle-Video API", "priority": "high", "hours": 8.0},
{"title": "Design Video Workflow UI", "priority": "medium", "hours": 12.0},
{"title": "Optimize Video Pipeline", "priority": "medium", "hours": 16.0},
]):
task = Task(
truth_node_id=charter.id,
project_id=pid,
title=task_item["title"],
required_skill_tag_ids=[],
status="todo",
priority=task_item["priority"],
estimated_hours=task_item["hours"],
)
session.add(task)
changed = True
print(f"[SSTC] 已为「{PIXELLE_NAME}」恢复宪章 1 个、任务 3 个")
return changed
async def _seed_projects(session: AsyncSession):
import json
result = await session.execute(
select(Project).where(
or_(Project.title == PIXELLE_NAME, Project.name == PIXELLE_NAME)
)
)
existing = result.scalar_one_or_none()
if existing:
restored = await _ensure_pixelle_sstc_data(session, existing)
if restored:
await session.commit()
else:
print("示例项目已存在且 SSTC 数据完整,跳过")
return
pixelle_signals = [
Signal(
source="HackerNews",
content="Pixelle-Video: An AI-powered automated short video generation platform with script-to-video automation",
impact_score=9.2,
priority="critical",
processed=True,
),
Signal(
source="ProductHunt",
content="Pixelle-Video launches API for programmatic video content generation",
impact_score=8.5,
priority="high",
processed=True,
),
Signal(
source="TechCrunch",
content="Pixelle-Video raises Series A funding to scale AI video infrastructure",
impact_score=7.8,
priority="high",
processed=True,
),
]
for signal in pixelle_signals:
signal.strategic_analysis = json.dumps({
"decision_proposal": {
"is_strategic": True,
"threat_level": "HIGH",
"lead_time_days": 14,
"impact_score": signal.impact_score,
"recommendation": "立即集成 Pixelle-Video API 到主平台"
},
"competitiveness_radar": {
"innovation": 85,
"market_potential": 90,
"technical_complexity": 75,
"resource_intensity": 70
},
"detected_tech_stacks": ["AI/ML", "Video Processing", "REST API"],
"related_knowledge_nodes": [],
"rag_context_used": 0
})
signal.tasks_generated = json.dumps([
{"task_name": "Integrate Pixelle-Video API", "type": "backend", "estimated_hours": 8.0},
{"task_name": "Design Video Workflow UI", "type": "frontend", "estimated_hours": 12.0},
{"task_name": "Optimize Video Pipeline", "type": "ai_engineer", "estimated_hours": 16.0},
])
session.add(signal)
await session.flush()
pixelle_project = Project(
name="Pixelle-Video 集成",
title="Pixelle-Video 集成",
description="集成 Pixelle-Video AI 视频生成平台到主系统",
status="active",
assigned_role="backend",
signal_count=len(pixelle_signals),
)
session.add(pixelle_project)
await session.flush()
for signal in pixelle_signals:
link = ProjectSignalLink(
project_id=pixelle_project.id,
signal_id=signal.id,
link_type="related",
relevance_score=1.0,
)
session.add(link)
ai_analyst_project = Project(
name="AI 分析引擎优化",
title="AI 分析引擎优化",
description="优化 LLM 服务性能,实现多模型投票机制和 RAG 检索增强",
status="active",
assigned_role="ai_engineer",
signal_count=1,
)
session.add(ai_analyst_project)
await session.flush()
ai_signal = Signal(
source="ArXiv",
content="New paper: Multi-Agent LLM Voting System improves accuracy by 23%",
impact_score=7.5,
priority="medium",
processed=True,
)
ai_signal.strategic_analysis = json.dumps({
"decision_proposal": {
"is_strategic": True,
"threat_level": "MEDIUM",
"lead_time_days": 30,
"impact_score": 7.5,
"recommendation": "实现多模型投票机制提升 AI 分析准确性"
},
"detected_tech_stacks": ["LLM", "Voting", "Multi-Agent"]
})
session.add(ai_signal)
await session.flush()
ai_link = ProjectSignalLink(
project_id=ai_analyst_project.id,
signal_id=ai_signal.id,
link_type="related",
relevance_score=0.9,
)
session.add(ai_link)
await session.flush()
# --- SSTC: 获取或创建默认用户,用于宪章 created_by ---
user_result = await session.execute(select(User).limit(1))
seed_user = user_result.scalar_one_or_none()
if not seed_user:
seed_user = User(
username="boss",
email="boss@pmflow.local",
password_hash=AuthService.hash_password("boss123"),
name="负责人",
role="BOSS",
status="active",
)
session.add(seed_user)
await session.flush()
# --- SSTC: 为 Pixelle-Video 创建宪章(关联信号)---
pixelle_signal_ids = [s.id for s in pixelle_signals]
charter = TruthNode(
node_type="charter",
parent_id=None,
project_id=pixelle_project.id,
created_by=seed_user.id,
title="Pixelle-Video 集成需求宪章",
content={
"rationale": "基于 HackerNews/ProductHunt/TechCrunch 信号,决定集成 Pixelle-Video API 到主平台",
"signal_ids": pixelle_signal_ids,
"decision": "立即集成 Pixelle-Video API,支持脚本到视频的自动化生成",
},
version=1,
status="aligned",
)
session.add(charter)
await session.flush()
# --- SSTC: 创建任务(绑定到宪章)---
for task_item in [
{"title": "Integrate Pixelle-Video API", "priority": "high", "hours": 8.0},
{"title": "Design Video Workflow UI", "priority": "medium", "hours": 12.0},
{"title": "Optimize Video Pipeline", "priority": "medium", "hours": 16.0},
]:
task = Task(
truth_node_id=charter.id,
project_id=pixelle_project.id,
title=task_item["title"],
required_skill_tag_ids=[],
status="todo",
priority=task_item["priority"],
estimated_hours=task_item["hours"],
)
session.add(task)
await session.commit()
print(f"创建示例项目:")
print(f" - {pixelle_project.title} ({pixelle_project.assigned_role})")
print(f" - {ai_analyst_project.title} ({ai_analyst_project.assigned_role})")
print(f"\n关联信号: {len(pixelle_signals) + 1} 条")
print(f"[SSTC] Pixelle-Video 宪章 1 个,任务 3 个")
async def main():
print("开始初始化示例数据...")
from app.database import engine
from app.models import Base
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
await seed_pixelle_project()
print("\n初始化完成!")
if __name__ == "__main__":
asyncio.run(main())