|
|
# backend/app/seed.py
|
|
|
from datetime import date
|
|
|
|
|
|
from sqlmodel import Session, select
|
|
|
|
|
|
from .db import engine, init_db
|
|
|
from .models import (
|
|
|
Approval,
|
|
|
Briefing,
|
|
|
ClsType,
|
|
|
Event,
|
|
|
Folder,
|
|
|
Goal,
|
|
|
InboxClassification,
|
|
|
InboxItem,
|
|
|
InboxKind,
|
|
|
InboxStatus,
|
|
|
Person,
|
|
|
Prio,
|
|
|
Project,
|
|
|
Sphere,
|
|
|
Task,
|
|
|
TaskComment,
|
|
|
TaskStatus,
|
|
|
)
|
|
|
|
|
|
YEAR = 2026
|
|
|
|
|
|
|
|
|
def D(mmdd: str | None):
|
|
|
"""'06-08' -> date(2026,6,8). 빈값/None -> None"""
|
|
|
if not mmdd:
|
|
|
return None
|
|
|
m, d = mmdd.split("-")
|
|
|
return date(YEAR, int(m), int(d))
|
|
|
|
|
|
|
|
|
# ---- people (REF/assets/tasks-data.js people) ----
|
|
|
PEOPLE = [
|
|
|
("jiwoo", "지우", "지", "var(--blue)", True),
|
|
|
("hyunwoo", "현우", "현", "var(--violet)", False),
|
|
|
("minseo", "민서", "민", "var(--green)", False),
|
|
|
("jaeho", "재호", "재", "var(--coral)", False),
|
|
|
("sua", "수아", "수", "oklch(0.66 0.13 200)", False),
|
|
|
]
|
|
|
|
|
|
# ---- folders + projects (REF tree) ----
|
|
|
FOLDERS = [
|
|
|
("work", "업무", "ink", "folder", 0),
|
|
|
("life", "개인", "blue", "heart", 1),
|
|
|
]
|
|
|
# (id, folder_id, parent_id, name, tone, sort)
|
|
|
PROJECTS = [
|
|
|
# 업무
|
|
|
("biz", "work", None, "경영 전략", "coral", 0),
|
|
|
("biz-okr", "work", "biz", "2026 Q2 OKR", "coral", 0),
|
|
|
("biz-report", "work", "biz", "분기 리포트", "coral", 1),
|
|
|
("biz-budget", "work", "biz", "예산 관리", "coral", 2),
|
|
|
("onb", "work", None, "온보딩 리디자인", "violet", 1),
|
|
|
("onb-research", "work", "onb", "사용자 리서치", "violet", 0),
|
|
|
("onb-wire", "work", "onb", "와이어프레임", "violet", 1),
|
|
|
("onb-voice", "work", "onb", "음성 인터페이스", "violet", 2),
|
|
|
("team", "work", None, "팀 운영", "green", 2),
|
|
|
("team-hr", "work", "team", "채용 & 온보딩", "green", 0),
|
|
|
("team-retro", "work", "team", "회고", "green", 1),
|
|
|
# 개인
|
|
|
("me", "life", None, "일상", "blue", 0),
|
|
|
("life-trip", "life", None, "여행 — 한국", "coral", 1),
|
|
|
("life-fam", "life", None, "가족", "green", 2),
|
|
|
]
|
|
|
|
|
|
# ---- tasks (REF tasks; 중첩) ----
|
|
|
TASKS = [
|
|
|
{
|
|
|
"id": "k1",
|
|
|
"title": "분기 리포트 초안 마무리",
|
|
|
"project": "biz-report",
|
|
|
"assignee": "jiwoo",
|
|
|
"due": "06-08",
|
|
|
"prio": "높음",
|
|
|
"status": "doing",
|
|
|
"notes": "<h3>목표</h3><p>경영진 미팅용 Q2 성과 리포트. <b>리텐션·매출·예산</b> 세 섹션으로 구성한다.</p><blockquote>핵심 지표는 시각화해서 한눈에 들어오게.</blockquote>",
|
|
|
"comments": [
|
|
|
{"who": "hyunwoo", "text": "매출 섹션은 제가 오늘 안으로 넘겨드릴게요."},
|
|
|
{"who": "jiwoo", "text": "좋아요, 예산 섹션만 확정되면 취합할게요."},
|
|
|
],
|
|
|
"children": [
|
|
|
{
|
|
|
"title": "리텐션 데이터 취합",
|
|
|
"status": "done",
|
|
|
"assignee": "sua",
|
|
|
"due": "06-08",
|
|
|
"project": "biz-report",
|
|
|
"children": [
|
|
|
{"title": "코호트 정의 확정", "status": "done", "assignee": "sua", "due": "06-07"},
|
|
|
{"title": "주차별 잔존율 추출", "status": "done", "assignee": "sua", "due": "06-08"},
|
|
|
{
|
|
|
"title": "이탈 사유 태깅",
|
|
|
"status": "done",
|
|
|
"assignee": "jaeho",
|
|
|
"due": "06-08",
|
|
|
"children": [
|
|
|
{"title": "인터뷰 발췌 5건", "status": "done", "assignee": "jaeho"},
|
|
|
{"title": "사유 카테고리 분류", "status": "done", "assignee": "jaeho"},
|
|
|
],
|
|
|
},
|
|
|
],
|
|
|
},
|
|
|
{
|
|
|
"title": "매출 섹션 작성",
|
|
|
"status": "doing",
|
|
|
"assignee": "jiwoo",
|
|
|
"due": "06-09",
|
|
|
"prio": "높음",
|
|
|
"project": "biz-report",
|
|
|
"notes": "<p>MRR/ARR 표는 완료. 증감 코멘트 마무리 필요.</p>",
|
|
|
"children": [
|
|
|
{"title": "MRR / ARR 표 정리", "status": "done", "assignee": "jiwoo"},
|
|
|
{"title": "전분기 대비 증감 코멘트", "status": "doing", "assignee": "jiwoo"},
|
|
|
{"title": "예측 시나리오 3종", "status": "todo", "assignee": "jiwoo"},
|
|
|
],
|
|
|
},
|
|
|
{"title": "예산 섹션 작성", "status": "todo", "assignee": "jiwoo", "due": "06-10", "project": "biz-budget"},
|
|
|
{"title": "경영진 검토 요청 메일", "status": "todo", "assignee": "jiwoo", "due": "06-10", "project": "biz-report"},
|
|
|
],
|
|
|
},
|
|
|
{
|
|
|
"id": "k2",
|
|
|
"title": "온보딩 와이어프레임 피드백 정리",
|
|
|
"project": "onb-wire",
|
|
|
"assignee": "hyunwoo",
|
|
|
"due": "06-09",
|
|
|
"prio": "높음",
|
|
|
"status": "review",
|
|
|
"notes": "<p>디자인 리뷰에서 나온 3개 화면 수정사항을 취합하고 우선순위를 매긴다.</p>",
|
|
|
"comments": [{"who": "sua", "text": "3번 화면 CTA 위치는 아래로 내리는 게 좋겠어요."}],
|
|
|
"children": [
|
|
|
{
|
|
|
"title": "환영 화면 수정안",
|
|
|
"status": "done",
|
|
|
"assignee": "hyunwoo",
|
|
|
"project": "onb-wire",
|
|
|
"children": [
|
|
|
{"title": "카피 톤 조정", "status": "done", "assignee": "hyunwoo"},
|
|
|
{"title": "일러스트 교체 요청", "status": "doing", "assignee": "minseo"},
|
|
|
],
|
|
|
},
|
|
|
{"title": "권한 요청 화면 재배치", "status": "doing", "assignee": "hyunwoo"},
|
|
|
{"title": "음성 안내 추가 검토", "status": "todo", "assignee": "sua"},
|
|
|
],
|
|
|
},
|
|
|
{
|
|
|
"id": "k5",
|
|
|
"title": "사용자 인터뷰 5건 정리",
|
|
|
"project": "onb-research",
|
|
|
"assignee": "sua",
|
|
|
"due": "06-10",
|
|
|
"prio": "보통",
|
|
|
"status": "doing",
|
|
|
"children": [
|
|
|
{"title": "녹취 요약 (5건)", "status": "doing", "assignee": "sua"},
|
|
|
{"title": "인사이트 태깅", "status": "todo", "assignee": "sua"},
|
|
|
{"title": "리서치 보드 업데이트", "status": "todo", "assignee": "minseo"},
|
|
|
],
|
|
|
},
|
|
|
{
|
|
|
"id": "k6",
|
|
|
"title": "OKR 중간 점검 자료 준비",
|
|
|
"project": "biz-okr",
|
|
|
"assignee": "jiwoo",
|
|
|
"due": "06-11",
|
|
|
"prio": "높음",
|
|
|
"status": "todo",
|
|
|
"children": [
|
|
|
{"title": "Objective별 진척도 집계", "status": "todo", "assignee": "jiwoo"},
|
|
|
{"title": "리스크 항목 표시", "status": "todo", "assignee": "jiwoo"},
|
|
|
],
|
|
|
},
|
|
|
{"id": "k4", "title": "구독 결제 카드 갱신", "project": "me", "assignee": "jiwoo", "due": "06-12", "prio": "보통", "status": "todo", "children": []},
|
|
|
{
|
|
|
"id": "k3",
|
|
|
"title": "신규 입사자 환영 메일 발송",
|
|
|
"project": "team-hr",
|
|
|
"assignee": "minseo",
|
|
|
"due": "06-07",
|
|
|
"prio": "보통",
|
|
|
"status": "done",
|
|
|
"children": [
|
|
|
{"title": "메일 템플릿 작성", "status": "done", "assignee": "minseo"},
|
|
|
{"title": "수신자 명단 확인", "status": "done", "assignee": "minseo"},
|
|
|
],
|
|
|
},
|
|
|
{
|
|
|
"id": "k13",
|
|
|
"title": "스프린트 회고 문서 배포",
|
|
|
"project": "team-retro",
|
|
|
"assignee": "minseo",
|
|
|
"due": "06-08",
|
|
|
"prio": "낮음",
|
|
|
"status": "review",
|
|
|
"children": [
|
|
|
{"title": "액션 아이템 담당자 지정", "status": "done", "assignee": "minseo"},
|
|
|
{"title": "다음 스프린트 반영 확인", "status": "doing", "assignee": "jaeho"},
|
|
|
],
|
|
|
},
|
|
|
{
|
|
|
"id": "k10",
|
|
|
"title": "모바일 푸시 알림 QA",
|
|
|
"project": "onb-wire",
|
|
|
"assignee": "sua",
|
|
|
"due": "06-09",
|
|
|
"prio": "높음",
|
|
|
"status": "doing",
|
|
|
"children": [
|
|
|
{"title": "iOS 시나리오 3종", "status": "done", "assignee": "sua"},
|
|
|
{"title": "Android 시나리오 3종", "status": "doing", "assignee": "sua"},
|
|
|
{"title": "딥링크 라우팅 확인", "status": "todo", "assignee": "jaeho"},
|
|
|
],
|
|
|
},
|
|
|
{
|
|
|
"id": "k14",
|
|
|
"title": "데이터 전처리 파이프라인",
|
|
|
"project": "onb-research",
|
|
|
"assignee": "minseo",
|
|
|
"due": "06-10",
|
|
|
"prio": "보통",
|
|
|
"status": "waiting",
|
|
|
"delegated": True,
|
|
|
"notes": "<p>일정이 빠듯해 같은 프로젝트의 <b>민서님</b>께 위임 — 현재 완료를 기다리는 중입니다.</p>",
|
|
|
"children": [
|
|
|
{"title": "원천 데이터 스키마 정리", "status": "done", "assignee": "minseo"},
|
|
|
{"title": "결측치 처리 규칙 정의", "status": "doing", "assignee": "minseo"},
|
|
|
{"title": "정제 스크립트 작성", "status": "todo", "assignee": "minseo"},
|
|
|
],
|
|
|
},
|
|
|
{
|
|
|
"id": "k20",
|
|
|
"title": "한국행 비행기 티켓 구매",
|
|
|
"project": "life-trip",
|
|
|
"assignee": "jiwoo",
|
|
|
"due": "06-14",
|
|
|
"prio": "높음",
|
|
|
"status": "todo",
|
|
|
"notes": "<p>스마트 인박스에서 자동 생성된 작업이에요. 아리가 <b>가격 추적 알림</b>을 켜뒀고, 적정가가 보이면 결재함으로 알려드려요.</p>",
|
|
|
"children": [
|
|
|
{"title": "날짜 후보 확정", "status": "doing", "assignee": "jiwoo", "project": "life-trip"},
|
|
|
{"title": "가격 알림 확인 후 결제", "status": "todo", "assignee": "jiwoo", "project": "life-trip"},
|
|
|
],
|
|
|
},
|
|
|
{"id": "k21", "title": "엄마 생신 선물 알아보기", "project": "life-fam", "assignee": "jiwoo", "due": "06-20", "prio": "보통", "status": "todo", "children": []},
|
|
|
]
|
|
|
|
|
|
# kx 카운터 (원본 tuid 와 동일하게 kx1,kx2,...)
|
|
|
_kx = 0
|
|
|
|
|
|
|
|
|
def kx() -> str:
|
|
|
global _kx
|
|
|
_kx += 1
|
|
|
return f"kx{_kx}"
|
|
|
|
|
|
|
|
|
# 댓글 TEXT PK 카운터 (c1, c2, ...)
|
|
|
_cidx = 0
|
|
|
|
|
|
|
|
|
def cid() -> str:
|
|
|
global _cidx
|
|
|
_cidx += 1
|
|
|
return f"c{_cidx}"
|
|
|
|
|
|
|
|
|
def insert_task(s: Session, node: dict, parent_id: str | None, project_id: str, order: int):
|
|
|
tid = node.get("id") or kx()
|
|
|
pid = node.get("project") or project_id # 상속 규칙
|
|
|
t = Task(
|
|
|
id=tid,
|
|
|
project_id=pid,
|
|
|
parent_id=parent_id,
|
|
|
title=node["title"],
|
|
|
status=TaskStatus(node.get("status", "todo")),
|
|
|
assignee_id=node.get("assignee", "jiwoo"),
|
|
|
due=D(node.get("due")),
|
|
|
prio=Prio(node.get("prio", "보통")),
|
|
|
notes=node.get("notes", ""),
|
|
|
est=node.get("est", ""),
|
|
|
delegated=node.get("delegated", False),
|
|
|
sort_order=order,
|
|
|
)
|
|
|
s.add(t)
|
|
|
for c in node.get("comments", []):
|
|
|
s.add(TaskComment(id=cid(), task_id=tid, person_id=c["who"], text=c["text"]))
|
|
|
for i, child in enumerate(node.get("children", [])):
|
|
|
insert_task(s, child, tid, pid, i)
|
|
|
|
|
|
|
|
|
# ---- inbox (REF sinbox-data.js) ----
|
|
|
INBOX = [
|
|
|
{
|
|
|
"id": "s1",
|
|
|
"kind": "text",
|
|
|
"status": "new",
|
|
|
"raw": "다음 주에 한국 놀러가는 비행기 티켓 사기",
|
|
|
"cls": {
|
|
|
"type": "task",
|
|
|
"sphere": "life",
|
|
|
"proj_label": "개인 › 여행 — 한국",
|
|
|
"project_id": "life-trip",
|
|
|
"tone": "coral",
|
|
|
"due_text": "출발 전 · ~6/14",
|
|
|
"when_text": "오늘 21:00 빈 시간 추천",
|
|
|
"extra": "가격 추적 알림 켜둠",
|
|
|
"reason": "구매라는 행동이 있으니 '작업' 맞아요. 작업 트리의 '개인' 아래에 '여행 — 한국' 프로젝트를 만들어 넣었어요 — 따로 섹션이 생기는 게 아니라 다른 작업과 똑같이 보여요. 출발까지 일주일이라 가격 알림도 걸어뒀어요.",
|
|
|
"confidence": 0.92,
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
"id": "s2",
|
|
|
"kind": "text",
|
|
|
"status": "confirmed",
|
|
|
"raw": "수요일 11시 자전거 수리 맡기기",
|
|
|
"cls": {
|
|
|
"type": "event",
|
|
|
"sphere": "life",
|
|
|
"proj_label": "개인 캘린더",
|
|
|
"project_id": None,
|
|
|
"tone": "blue",
|
|
|
"due_text": "수 6/10 11:00",
|
|
|
"when_text": "캘린더 등록 완료",
|
|
|
"extra": "",
|
|
|
"reason": "시간이 정해진 일은 작업이 아니라 일정으로 바로 등록해요.",
|
|
|
"confidence": 0.95,
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
"id": "s3",
|
|
|
"kind": "voice",
|
|
|
"status": "confirmed",
|
|
|
"raw": "음성 메모 0:09 — 엄마 생신 선물 미리 알아보기",
|
|
|
"cls": {
|
|
|
"type": "task",
|
|
|
"sphere": "life",
|
|
|
"proj_label": "가족",
|
|
|
"project_id": "life-fam",
|
|
|
"tone": "green",
|
|
|
"due_text": "6/20 전",
|
|
|
"when_text": "주말 오전 블록",
|
|
|
"extra": "",
|
|
|
"reason": "기한이 느슨한 개인 작업이라 주말 블록에 배치했어요.",
|
|
|
"confidence": 0.8,
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
"id": "s4",
|
|
|
"kind": "text",
|
|
|
"status": "confirmed",
|
|
|
"raw": "온보딩 환영 화면에 짧은 애니메이션 넣으면 어떨까",
|
|
|
"cls": {
|
|
|
"type": "idea",
|
|
|
"sphere": "work",
|
|
|
"proj_label": "온보딩 리디자인 · 아이디어 보드",
|
|
|
"project_id": "onb",
|
|
|
"tone": "violet",
|
|
|
"due_text": "",
|
|
|
"when_text": "",
|
|
|
"extra": "",
|
|
|
"reason": "아직 행동이 정해지지 않아 보드에 보관 — 목요일 디자인 싱크 안건으로도 제안해둘게요.",
|
|
|
"confidence": 0.7,
|
|
|
},
|
|
|
},
|
|
|
]
|
|
|
|
|
|
# ---- dashboard 읽기전용 (REF data.js / approve-data.js) ----
|
|
|
# (id, time, title, tag, dur, tone키, soon)
|
|
|
SCHEDULE = [
|
|
|
("e1", "09:30", "팀 데일리 스탠드업", "프로덕트", "15분", "blue", False),
|
|
|
("e2", "11:00", "디자인 리뷰 — 온보딩 플로우", "디자인", "45분", "violet", False),
|
|
|
("e3", "14:00", "분기 전략 미팅", "경영진", "60분", "coral", True),
|
|
|
("e4", "16:30", "1:1 — 민서님", "팀", "30분", "green", False),
|
|
|
]
|
|
|
# (id, title, pct, sub, tone키)
|
|
|
GOALS = [
|
|
|
("g1", "분기 OKR — 사용자 리텐션", 68, "12개 중 8개 달성", "blue"),
|
|
|
("g2", "주 4회 운동", 75, "이번 주 3/4회", "coral"),
|
|
|
("g3", "‘딥 워크’ 책 완독", 40, "320쪽 중 128쪽", "violet"),
|
|
|
]
|
|
|
APPROVALS = [
|
|
|
("a1", "cal", "coral", "low", "07:42", "치과 예약을 16:00로 옮겼어요", "14시 분기 전략 미팅과 겹침 · 병원 예약 시스템에서 빈 슬롯 확인 후 변경", "", "", "원래 시간으로"),
|
|
|
("a2", "mail", "blue", "low", "06:10", "영수증·뉴스레터 7통을 정리했어요", "영수증 3통 → 금융 폴더 · 뉴스레터 4통 → 읽을거리, 받은편지함은 중요한 것만 남김", "", "", "되돌리기"),
|
|
|
("a3", "cal", "violet", "low", "07:40", "내일 오전 딥 워크 2시간을 예약했어요", "분기 리포트 마감(내일 18시) 대비 · 9:00–11:00, 방해 금지로 설정", "", "", "블록 해제"),
|
|
|
("a4", "mail", "violet", "high", "보내기 대기", "현우님께 회신 초안이 준비됐어요", "“잘 받았어요! 금요일 오전까지 화면별 코멘트 정리해서 드릴게요.”", "보내기", "수정", ""),
|
|
|
("a5", "users", "green", "high", "전달 대기", "민서님께 ‘데이터 전처리’ 위임 요청", "오늘 일정 과부하 감지 · 맥락 요약과 마감(목)을 담은 요청 메시지 작성 완료", "전달", "내가 할게", ""),
|
|
|
("a6", "wallet", "amber", "high", "확인 필요", "Netflix 일시정지를 추천해요", "최근 2개월 시청 기록 없음 · 모레 17,000원 결제 예정 — 정지 절차는 준비해뒀어요", "일시정지", "유지", ""),
|
|
|
]
|
|
|
BRIEFING = dict(
|
|
|
today="6월 7일 일요일",
|
|
|
weather_temp=24,
|
|
|
weather_cond="맑음 · 한낮 28°",
|
|
|
weather_icon="cloudSun",
|
|
|
commute="출근 23분 · 평소보다 4분 빠름",
|
|
|
sleep="어젯밤 7시간 12분 · 평소만큼 푹 잤어요",
|
|
|
note="오늘은 오후 미팅이 핵심이에요. 오전을 비워 <b>분기 리포트</b>에 집중하시면 좋겠어요. 14시 전엔 비가 그칠 예정이라 우산은 안 챙기셔도 돼요.",
|
|
|
saved_today="47분",
|
|
|
today_routed=7,
|
|
|
)
|
|
|
|
|
|
|
|
|
def _seed_dashboard(s: Session) -> None:
|
|
|
"""대시보드 읽기전용 시드 — run_seed 내부에서만 호출하는 헬퍼."""
|
|
|
for i, (eid, t, title, tag, dur, tone, soon) in enumerate(SCHEDULE):
|
|
|
s.add(Event(id=eid, time=t, title=title, tag=tag, dur=dur, tone=tone, soon=soon, sort_order=i))
|
|
|
for i, (gid, title, pct, sub, tone) in enumerate(GOALS):
|
|
|
s.add(Goal(id=gid, title=title, pct=pct, sub=sub, tone=tone, sort_order=i))
|
|
|
for i, row in enumerate(APPROVALS):
|
|
|
aid, icon, tone, risk, time, title, detail, cta, alt, undo = row
|
|
|
s.add(
|
|
|
Approval(
|
|
|
id=aid, icon=icon, tone=tone, risk=risk, time=time, title=title,
|
|
|
detail=detail, cta=cta, alt=alt, undo_label=undo, sort_order=i,
|
|
|
)
|
|
|
)
|
|
|
s.add(Briefing(id=1, **BRIEFING)) # 단일 row id=1
|
|
|
|
|
|
|
|
|
def _reset_counters() -> None:
|
|
|
global _kx, _cidx
|
|
|
_kx = 0
|
|
|
_cidx = 0
|
|
|
|
|
|
|
|
|
def _run(s: Session, reset: bool) -> None:
|
|
|
"""주어진 세션 위에서 시드 적재(트랜잭션 본문)."""
|
|
|
_reset_counters()
|
|
|
if reset:
|
|
|
for tbl in (
|
|
|
TaskComment, Task, InboxClassification, InboxItem,
|
|
|
Project, Folder, Person, Event, Approval, Goal, Briefing,
|
|
|
):
|
|
|
for row in s.exec(select(tbl)).all():
|
|
|
s.delete(row)
|
|
|
s.commit()
|
|
|
|
|
|
for pid, name, ini, color, me in PEOPLE:
|
|
|
s.add(Person(id=pid, name=name, initial=ini, color=color, is_me=me))
|
|
|
for fid, name, tone, icon, order in FOLDERS:
|
|
|
s.add(Folder(id=fid, name=name, tone=tone, icon=icon, sort_order=order))
|
|
|
for pid, fid, parent, name, tone, order in PROJECTS:
|
|
|
s.add(Project(id=pid, folder_id=fid, parent_id=parent, name=name, tone=tone, sort_order=order))
|
|
|
s.commit()
|
|
|
|
|
|
for i, node in enumerate(TASKS):
|
|
|
insert_task(s, node, None, node["project"], i)
|
|
|
s.commit()
|
|
|
|
|
|
for n, it in enumerate(INBOX, start=1):
|
|
|
s.add(InboxItem(id=it["id"], kind=InboxKind(it["kind"]), raw=it["raw"], status=InboxStatus(it["status"])))
|
|
|
c = it["cls"]
|
|
|
s.add(
|
|
|
InboxClassification(
|
|
|
id=f"cls{n}", # TEXT PK
|
|
|
inbox_item_id=it["id"], type=ClsType(c["type"]), sphere=Sphere(c["sphere"]),
|
|
|
project_id=c["project_id"], proj_label=c["proj_label"], tone=c["tone"],
|
|
|
due_text=c["due_text"], when_text=c["when_text"], extra=c["extra"],
|
|
|
reason=c["reason"], confidence=c["confidence"], model="seed",
|
|
|
)
|
|
|
)
|
|
|
s.commit()
|
|
|
|
|
|
_seed_dashboard(s) # 대시보드 읽기전용 시드 통합
|
|
|
s.commit()
|
|
|
|
|
|
|
|
|
def run_seed(session: Session | None = None, reset: bool = True) -> None:
|
|
|
"""시드 진입 함수 단일 정본.
|
|
|
|
|
|
- CLI: run_seed() → 자체 세션 생성(엔진에서).
|
|
|
- 테스트: run_seed(session=test_session, reset=True) → 주어진 세션 재사용.
|
|
|
"""
|
|
|
init_db()
|
|
|
if session is not None:
|
|
|
_run(session, reset)
|
|
|
else:
|
|
|
with Session(engine) as s:
|
|
|
_run(s, reset)
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
run_seed()
|
|
|
print("✅ seed 완료")
|