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.

70 lines
2.5 KiB
Python

# backend/app/automation/suggester.py
# 반복 패턴 탐지 → automation_suggestion. notification.triaged 구독 + 수동 scan 둘 다 지원.
from __future__ import annotations
import uuid
from sqlmodel import Session, select
from ..models import AutomationRunLog, AutomationSuggestion
from .event_bus import Event, EventBus
from .events import AUTOMATION_SUGGESTED, NOTIFICATION_TRIAGED
# 데모용 결정적 패턴 규칙: (감지 텍스트 부분일치, 제안)
PATTERNS = [
{
"match": "다이제스트",
"pattern": "저녁 다이제스트를 자주 쓰시네요.",
"offer": {
"name": "아침에도 다이제스트",
"cat": "mail",
"trigger": "08:00",
"cond": None,
"action": "밤사이 메일 묶음 브리핑",
},
},
]
def scan(s: Session, bus: EventBus | None = None) -> list[AutomationSuggestion]:
"""run_log 를 훑어 반복 패턴이 임계 이상이면 제안 생성(중복 방지).
생성된 제안마다 automation.suggested 발행(bus 가 주어지면)."""
logs = s.exec(select(AutomationRunLog)).all()
existing = {x.offer_name for x in s.exec(select(AutomationSuggestion)).all()}
created: list[AutomationSuggestion] = []
for pat in PATTERNS:
hits = [log for log in logs if pat["match"] in (log.text or "")]
if len(hits) >= 3 and pat["offer"]["name"] not in existing: # 임계=3
o = pat["offer"]
sug = AutomationSuggestion(
id="asug-" + uuid.uuid4().hex[:8],
pattern=pat["pattern"],
offer_name=o["name"],
offer_cat=o["cat"],
offer_trigger=o["trigger"],
offer_cond=o["cond"],
offer_action=o["action"],
status="open",
)
s.add(sug)
created.append(sug)
s.commit()
if bus:
for sug in created:
bus.publish(
AUTOMATION_SUGGESTED,
{"suggestion_id": sug.id, "offer_name": sug.offer_name, "pattern": sug.pattern},
)
return created
def register(bus: EventBus, session_factory) -> None:
"""버스에 suggester 핸들러 등록 — notification.triaged 구독.
phase-9 알림 트리아지의 '나중에' 반복 패턴을 입력으로 scan → automation.suggested 체인."""
def on_triaged(ev: Event) -> None:
with session_factory() as s:
scan(s, bus)
bus.subscribe(NOTIFICATION_TRIAGED, on_triaged)