35 lines
1.1 KiB
Python
35 lines
1.1 KiB
Python
"""브라우저용 세션 projection이 공유하는 순수 도우미."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
from typing import cast
|
|
|
|
from .services import state_machine
|
|
from .stage_contract import StageLabel, stage_label as _normalize_stage_label
|
|
from .store import InProcSession, TurnRecord
|
|
|
|
LEARNER_VISIBLE_AI_ROLE = "counselor"
|
|
|
|
# 전 주기(라포→정리) 기준 라포 만점: 마지막 전이 임계(개입→정리)를 100으로 본다.
|
|
_FULL_CYCLE_RAPPORT = max(state_machine.STAGE_ADVANCE_RAPPORT.values())
|
|
|
|
|
|
def stage_label(stage: object) -> StageLabel:
|
|
return cast(StageLabel, _normalize_stage_label(stage))
|
|
|
|
|
|
def iso(ts: float | None) -> str | None:
|
|
if ts is None:
|
|
return None
|
|
return datetime.fromtimestamp(ts, tz=timezone.utc).isoformat(timespec="seconds")
|
|
|
|
|
|
def learner_visible_turns(sess: InProcSession) -> list[TurnRecord]:
|
|
return sess.turns_visible_to(LEARNER_VISIBLE_AI_ROLE)
|
|
|
|
|
|
def _rapport_percent(credit: float) -> int:
|
|
if _FULL_CYCLE_RAPPORT <= 0:
|
|
return 0
|
|
return max(0, min(100, int(round(float(credit or 0.0) / _FULL_CYCLE_RAPPORT * 100))))
|