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.

62 lines
2.0 KiB
Python

# backend/app/services/focus.py
# 빈 블록 계산 → 집중 블록 제안 (free-slot finder).
from __future__ import annotations
from sqlmodel import Session, select
from ..models import CalEvent, FocusBlock
DAY_START_MIN = 9 * 60 # 09:00 (근무 시작)
DAY_END_MIN = 18 * 60 # 18:00
DEEP_THRESHOLD = 60 # 60분 이상 빈 블록 → deep, 미만 → light
def _to_min(t: str) -> int:
h, m = map(int, t.split(":"))
return h * 60 + m
def _to_hhmm(mins: int) -> str:
return f"{mins // 60:02d}:{mins % 60:02d}"
def free_slots(s: Session, day: int) -> list[tuple[int, int]]:
"""그 날 09:00~18:00 중 이벤트가 없는 [start_min, end_min) 구간 목록."""
evs = s.exec(select(CalEvent).where(CalEvent.day == day)).all()
busy = sorted([(_to_min(e.start), _to_min(e.end)) for e in evs])
slots: list[tuple[int, int]] = []
cur = DAY_START_MIN
for bs, be in busy:
if bs > cur:
slots.append((cur, min(bs, DAY_END_MIN)))
cur = max(cur, be)
if cur >= DAY_END_MIN:
break
if cur < DAY_END_MIN:
slots.append((cur, DAY_END_MIN))
return [(a, b) for a, b in slots if b > a]
def suggest_focus(s: Session, day: int, min_minutes: int = 20) -> list[FocusBlock]:
"""빈 슬롯에 집중 블록 제안. 길이로 deep/light 결정(저장은 라우터 create 옵션)."""
out: list[FocusBlock] = []
for i, (a, b) in enumerate(free_slots(s, day)):
length = b - a
if length < min_minutes:
continue
is_deep = length >= DEEP_THRESHOLD
out.append(
FocusBlock(
id=f"fs{day}-{i}",
day=day,
start=_to_hhmm(a),
end=_to_hhmm(min(a + (75 if is_deep else 20), b)),
title="딥 워크 블록" if is_deep else "가벼운 일 처리",
type="deep" if is_deep else "light",
tag="딥 워크" if is_deep else "가벼운 일",
auto=True,
sort_order=i,
)
)
return out