You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

190 lines
5.9 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# backend/app/services/journey.py — 여정 집계
from sqlmodel import Session, select
from ..models import Event, JourneyLink, Person, Task, TaskStatus
from ..schemas import (
GaugeOut,
JourneyCardsOut,
JourneyHeaderOut,
JourneyLinkOut,
JourneyMetaOut,
JourneyOut,
JourneyPersonOut,
JourneyRowOut,
JourneyStageOut,
JourneyTableOut,
MeetCardOut,
RoutineCardOut,
TaskCardOut,
TileCardOut,
)
STAGES = [
("s1", "아침 준비", "06:40 09:00", "blue", "2"),
("s2", "오전 집중", "09:00 13:00", "coral", "4"),
("s3", "오후 협업", "13:00 17:00", "violet", "4"),
("s4", "마무리 & 내일", "17:00 23:00", "ink", "4"),
]
ROUTINE_CARDS = [
dict(
node="m-sleep",
icon="moon",
title="수면 7시간 12분",
meta="평소만큼 푹 잔 밤",
tag="컨디션 좋음",
tool="tick",
toolTone="ok",
),
dict(
node="m-brief",
icon="sun",
title="아침 브리핑 확인",
meta="맑음 24° · 출근 23분",
tag="4분 빠름",
tool="cal",
toolTone="",
),
]
TASK_NODES = [
("t-report", "k1", "경영 전략 · 우선"),
("t-wire", "k2", "온보딩 · 우선"),
("t-interview", "k5", "온보딩 · 리서치"),
("t-welcome", "k3", "팀 운영"),
]
MEET_NODES = [
("k-stand", "e1", "minseo", False),
("k-design", "e2", "hyunwoo", False),
("k-strat", "e3", "jaeho", True),
("k-11", "e4", "minseo", False),
]
TILE_CARDS = [
dict(node="w-focus", hi=True, icon="target", tt="지금 집중", ts="분기 리포트 마무리"),
dict(node="w-retro", hi=False, icon="send", tt="회고 배포", ts="팀 채널 공유"),
dict(node="w-health", hi=False, icon="heart", tt="운동 30분", ts="주 3/4회"),
dict(node="w-next", hi=False, icon="cal", tt="내일 준비", ts="일정 4건"),
]
PEOPLE_TASKCOUNT = {"jiwoo": 4, "hyunwoo": 2, "minseo": 2, "jaeho": 2, "sua": 3}
PEOPLE_ROLE = {
"jiwoo": "나 · PM",
"hyunwoo": "디자인",
"minseo": "팀 운영",
"jaeho": "데이터",
"sua": "리서치",
}
TABLE_ROWS = [
("t-report", "k1", True, "09:00"),
("t-wire", "k2", False, "11:00"),
("t-interview", "k5", False, "13:30"),
("t-welcome", "k3", True, "08:30"),
("t-card", "k4", False, "18:00"),
]
STATUS_CHIP = {
"doing": ("prog", "진행중"),
"review": ("review", "검토"),
"done": ("done", "완료"),
"todo": ("sched", "예정"),
"waiting": ("prog", "진행중"),
}
def build_journey(s: Session) -> JourneyOut:
people = s.exec(select(Person)).all()
tasks = {t.id: t for t in s.exec(select(Task)).all()}
events = {e.id: e for e in s.exec(select(Event)).all()}
# 여정 협업 인물은 핵심 5인만(메일 발신자 제외)
core = {"jiwoo", "hyunwoo", "minseo", "jaeho", "sua"}
person_out = [
JourneyPersonOut(
id=p.id,
name=p.name,
initial=p.initial,
color=p.color,
me=p.is_me,
tasks=PEOPLE_TASKCOUNT.get(p.id, 0),
role=PEOPLE_ROLE.get(p.id, ""),
)
for p in people
if p.id in core
]
stages = [
JourneyStageOut(id=i, title=t, time=tm, tone=tone, n=n) for (i, t, tm, tone, n) in STAGES
]
s1 = [RoutineCardOut(**c) for c in ROUTINE_CARDS]
s2 = []
for node, tid, meta in TASK_NODES:
t = tasks.get(tid)
s2.append(
TaskCardOut(
node=node,
title=t.title if t else node,
meta=meta,
who=t.assignee_id if t else "jiwoo",
done=(t.status == TaskStatus.done) if t else False,
)
)
s3 = []
for node, eid, who, soon in MEET_NODES:
e = events.get(eid)
meta = f"{e.tag} · {e.dur}" if e else ""
s3.append(
MeetCardOut(
node=node,
time=e.time if e else "",
title=e.title if e else node,
meta=meta,
who=who,
soon=soon,
)
)
s4 = [TileCardOut(**c) for c in TILE_CARDS]
links = [
JourneyLinkOut(
from_node=link.from_node, to_node=link.to_node, tone=link.tone, dash=link.dash
)
for link in s.exec(select(JourneyLink).order_by(JourneyLink.sort_order)).all()
]
rows = []
for node, tid, star, start in TABLE_ROWS:
t = tasks.get(tid)
chip, label = STATUS_CHIP.get(t.status.value if t else "todo", ("sched", "예정"))
rows.append(
JourneyRowOut(
node=node,
title=(t.title if t else node),
star=star,
statusKey=chip,
statusLabel=label,
startTime=start,
who=(t.assignee_id if t else "jiwoo"),
)
)
counts = {"prog": 0, "review": 0, "done": 0, "sched": 0}
for r in rows:
counts[r.statusKey] = counts.get(r.statusKey, 0) + 1
subtitle = (
f"{len(rows)}건 · 진행중 {counts['prog']} · 검토 {counts['review']} "
f"· 완료 {counts['done']} · 예정 {counts['sched']}"
)
gauges = [
GaugeOut(pct=87, tone="blue", val="5.2", unit="시간", label="딥 워크", sub="목표 6시간"),
GaugeOut(pct=72, tone="coral", val="72", unit="%", label="활동 링", sub="7.2천 걸음"),
]
return JourneyOut(
header=JourneyHeaderOut(dateLabel="6월 10일 화요일", weather="맑음 · 한낮 28°"),
meta=JourneyMetaOut(stageCount=4, itemCount=12, collabCount=5, peopleMore="+3"),
people=person_out,
stages=stages,
cards=JourneyCardsOut(s1=s1, s2=s2, s3=s3, s4=s4),
links=links,
rows=rows,
table=JourneyTableOut(subtitle=subtitle),
gauges=gauges,
gaugeFoot="오후 3시예요 — 물 한 잔, 5분 스트레칭 어때요?",
)