feat(realestate): notify_new_listings — 매물 알림 agent-office push
This commit is contained in:
@@ -3,7 +3,8 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from .db import get_profile, get_unnotified_matches, mark_matches_notified
|
from .db import (get_profile, get_unnotified_matches, mark_matches_notified,
|
||||||
|
get_listing_criteria, get_unnotified_listing_matches, mark_listings_notified)
|
||||||
|
|
||||||
logger = logging.getLogger("realestate-lab")
|
logger = logging.getLogger("realestate-lab")
|
||||||
|
|
||||||
@@ -44,3 +45,27 @@ def notify_new_matches() -> dict:
|
|||||||
mark_matches_notified(sent_ids)
|
mark_matches_notified(sent_ids)
|
||||||
logger.info("알림 송신: %d건", len(sent_ids))
|
logger.info("알림 송신: %d건", len(sent_ids))
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
def notify_new_listings() -> dict:
|
||||||
|
"""임계 통과 미알림 매물을 agent-office로 push (notify_new_matches 대칭).
|
||||||
|
passed 매물은 전 안전등급 알림(위험도 회피 목적 유용). criteria.min_safety_tier 설정 시 그 이상만."""
|
||||||
|
crit = get_listing_criteria()
|
||||||
|
if not crit.get("notify_enabled"):
|
||||||
|
return {"sent": 0, "skipped": "notify_disabled"}
|
||||||
|
listings = get_unnotified_listing_matches(min_tier=crit.get("min_safety_tier"))
|
||||||
|
if not listings:
|
||||||
|
return {"sent": 0}
|
||||||
|
url = f"{AGENT_OFFICE_URL}/api/agent-office/realestate/notify-listing"
|
||||||
|
try:
|
||||||
|
resp = requests.post(url, json={"listings": listings}, timeout=NOTIFY_TIMEOUT_SECONDS)
|
||||||
|
resp.raise_for_status()
|
||||||
|
body = resp.json()
|
||||||
|
except requests.RequestException as e:
|
||||||
|
logger.error("agent-office 매물 push 실패: %s", e)
|
||||||
|
return {"sent": 0, "error": str(e)}
|
||||||
|
sent_ids = body.get("sent_ids") or []
|
||||||
|
if sent_ids:
|
||||||
|
mark_listings_notified(sent_ids)
|
||||||
|
logger.info("매물 알림 송신: %d건", len(sent_ids))
|
||||||
|
return body
|
||||||
|
|||||||
28
realestate-lab/tests/test_listing_notifier.py
Normal file
28
realestate-lab/tests/test_listing_notifier.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
|
|
||||||
|
def _seed_passed_match():
|
||||||
|
from app import db
|
||||||
|
row, _ = db.upsert_listing({"article_no": "N1", "source": "naver", "deal_type": "전세",
|
||||||
|
"deposit": 29000, "area_exclusive": 42.0, "dong": "신대방동"})
|
||||||
|
db.upsert_listing_match({"listing_id": row["id"], "category": "임차", "passed": 1,
|
||||||
|
"match_score": 100, "safety_tier": "안전", "sample_size": 7,
|
||||||
|
"reasons": ["ok"], "is_new": 1})
|
||||||
|
return db.get_unnotified_listing_matches()[0]["id"]
|
||||||
|
|
||||||
|
def test_notify_listings_pushes_and_marks():
|
||||||
|
from app import notifier, db
|
||||||
|
mid = _seed_passed_match()
|
||||||
|
fake = MagicMock(); fake.json.return_value = {"sent": 1, "sent_ids": [mid]}; fake.raise_for_status.return_value = None
|
||||||
|
with patch.object(notifier.requests, "post", return_value=fake) as post:
|
||||||
|
res = notifier.notify_new_listings()
|
||||||
|
assert post.call_count == 1
|
||||||
|
assert db.get_unnotified_listing_matches() == [] # 마킹됨
|
||||||
|
|
||||||
|
def test_notify_listings_no_mark_on_failure():
|
||||||
|
from app import notifier, db
|
||||||
|
import requests as rq
|
||||||
|
mid = _seed_passed_match()
|
||||||
|
with patch.object(notifier.requests, "post", side_effect=rq.RequestException("down")):
|
||||||
|
notifier.notify_new_listings()
|
||||||
|
assert len(db.get_unnotified_listing_matches()) == 1 # 미마킹→재시도
|
||||||
Reference in New Issue
Block a user