perf(realestate): run_listing_matching 단일 conn+배치 + POST /listings/rematch(MOLIT없이 즉시 재판정)
- get_market_deals_for에 optional conn 파라미터 추가(재사용 시 open/close 안 함, 기존 단건 호출부는 불변) - bulk_upsert_listing_matches 추가(listing_matches 다건 upsert, 단일 connection) - run_listing_matching이 listings 조회+매물별 market_deals 조회+최종 upsert를 단일 _conn()으로 처리 (기존: 269건 매물마다 개별 _conn() 3회 → 병목) - POST /api/realestate/listings/rematch: MOLIT 재수집 없이 기존 listings+market_deals로 즉시 재판정 (알림 미발송, 즉시 피드백용) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
This commit is contained in:
@@ -27,3 +27,53 @@ def test_match_listing_rent_deposit_cap():
|
||||
over = match_listing(crit, {"deal_type": "전세", "deposit": 40000, "area_exclusive": 42.0,
|
||||
"dong": "신대방동"}, budget={"max_deposit": 45000})
|
||||
assert over["passed"] is False
|
||||
|
||||
|
||||
# ── Task 2: run_listing_matching 단일 conn 통합 테스트 ───────────────────────
|
||||
|
||||
def test_run_listing_matching_end_to_end_safe_rent_and_overpriced_sale():
|
||||
"""listings+market_deals+criteria를 시드하고 run_listing_matching()을 돌려
|
||||
listing_matches가 실제로 생성되고 tier/passed가 기대대로인지 확인한다
|
||||
(단일 connection 리팩터 후에도 판정 로직이 보존됐는지 검증하는 회귀 테스트)."""
|
||||
from app import db
|
||||
from app.listing_matcher import run_listing_matching
|
||||
|
||||
# criteria: 신대방동 전세/매매, 보증금 상한 32000, 최소면적 40
|
||||
db.update_listing_criteria({
|
||||
"dongs": ["신대방동"], "deal_types": ["전세", "매매"],
|
||||
"max_deposit": 32000, "min_area": 40.0,
|
||||
"house_types": ["아파트"], "equity": 20000, "annual_income": 6000,
|
||||
})
|
||||
|
||||
# 전세 실거래 시세(median ~42000) — 안전 매물(28000, ratio 0.67)
|
||||
for i, v in enumerate((40000, 42000, 44000)):
|
||||
db.upsert_market_deal({"house_type": "아파트", "dong_code": "11590", "dong": "신대방동",
|
||||
"complex_name": "OO", "area": 42.0, "deal_type": "전세",
|
||||
"deposit": v, "sale_amount": None,
|
||||
"deal_ym": f"2026{i+1:02d}", "floor": "5", "source": "molit"})
|
||||
l_rent, _ = db.upsert_listing({"article_no": "R1", "source": "naver", "deal_type": "전세",
|
||||
"deposit": 28000, "area_exclusive": 42.0,
|
||||
"complex_name": "OO", "dong": "신대방동", "dong_code": "11590"})
|
||||
|
||||
# 매매 실거래 시세(median ~87500) — 고가 매물(95000, ratio 1.09)
|
||||
for i, v in enumerate((80000, 88000, 90000, 87000)):
|
||||
db.upsert_market_deal({"house_type": "아파트", "dong_code": "11590", "dong": "신대방동",
|
||||
"complex_name": "PP", "area": 59.0, "deal_type": "매매",
|
||||
"deposit": None, "sale_amount": v,
|
||||
"deal_ym": f"2026{i+1:02d}", "floor": "10", "source": "molit"})
|
||||
l_sale, _ = db.upsert_listing({"article_no": "R2", "source": "naver", "deal_type": "매매",
|
||||
"sale_price": 95000, "area_exclusive": 59.0,
|
||||
"complex_name": "PP", "dong": "신대방동", "dong_code": "11590"})
|
||||
|
||||
run_listing_matching()
|
||||
|
||||
matches = {m["listing_id"]: m for m in db.get_listing_matches()}
|
||||
assert len(matches) == 2
|
||||
rent_m = matches[l_rent["id"]]
|
||||
assert rent_m["passed"] == 1
|
||||
assert rent_m["safety_tier"] == "안전"
|
||||
assert rent_m["sample_size"] == 3
|
||||
|
||||
sale_m = matches[l_sale["id"]]
|
||||
assert sale_m["valuation_tier"] == "고가"
|
||||
assert sale_m["sample_size"] == 4
|
||||
|
||||
Reference in New Issue
Block a user