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
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)
|