feat(realestate): 5축 점수 breakdown + 대시보드 pass_count
- matcher: _compute_score()에 score_breakdown {region/type/area/price/eligibility} 반환
- matcher: run_matching() DB INSERT에 score_breakdown JSON 저장
- db: match_results에 score_breakdown 컬럼 마이그레이션
- db: _enrich_items / get_matches에서 score_breakdown 파싱 포함
- db: get_matches에 a.district 컬럼 추가
- db: get_dashboard()에 pass_count (min_match_score 임계값 통과 건수) 추가
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -159,6 +159,12 @@ def init_db():
|
||||
except Exception:
|
||||
conn.execute("ALTER TABLE match_results ADD COLUMN notified_at TEXT")
|
||||
|
||||
# ── 마이그레이션: score_breakdown 컬럼 추가 ──
|
||||
try:
|
||||
conn.execute("SELECT score_breakdown FROM match_results LIMIT 1")
|
||||
except Exception:
|
||||
conn.execute("ALTER TABLE match_results ADD COLUMN score_breakdown TEXT")
|
||||
|
||||
# ── collect_log ──────────────────────────────────────────────────
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS collect_log (
|
||||
@@ -281,13 +287,14 @@ def _enrich_items(conn, items: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||
# 매칭 점수
|
||||
if ann_id:
|
||||
match_row = conn.execute(
|
||||
"SELECT match_score, match_reasons, eligible_types FROM match_results WHERE announcement_id = ?",
|
||||
"SELECT match_score, match_reasons, eligible_types, score_breakdown FROM match_results WHERE announcement_id = ?",
|
||||
(ann_id,),
|
||||
).fetchone()
|
||||
if match_row:
|
||||
item["match_score"] = match_row["match_score"]
|
||||
item["match_reasons"] = json.loads(match_row["match_reasons"]) if match_row["match_reasons"] else []
|
||||
item["eligible_types"] = json.loads(match_row["eligible_types"]) if match_row["eligible_types"] else []
|
||||
item["score_breakdown"] = json.loads(match_row["score_breakdown"]) if match_row["score_breakdown"] else None
|
||||
return items
|
||||
|
||||
|
||||
@@ -681,7 +688,7 @@ def get_matches(page: int = 1, size: int = 20) -> Dict[str, Any]:
|
||||
|
||||
total = conn.execute("SELECT COUNT(*) FROM match_results").fetchone()[0]
|
||||
rows = conn.execute("""
|
||||
SELECT m.*, a.house_nm, a.region_name, a.address, a.status as ann_status,
|
||||
SELECT m.*, a.house_nm, a.region_name, a.address, a.district, a.status as ann_status,
|
||||
a.receipt_start, a.receipt_end, a.winner_date, a.pblanc_url,
|
||||
a.house_secd, a.is_speculative_area
|
||||
FROM match_results m
|
||||
@@ -695,6 +702,7 @@ def get_matches(page: int = 1, size: int = 20) -> Dict[str, Any]:
|
||||
d = {c: r[c] for c in r.keys()}
|
||||
d["match_reasons"] = json.loads(d["match_reasons"]) if d["match_reasons"] else []
|
||||
d["eligible_types"] = json.loads(d["eligible_types"]) if d["eligible_types"] else []
|
||||
d["score_breakdown"] = json.loads(d["score_breakdown"]) if d.get("score_breakdown") else None
|
||||
items.append(d)
|
||||
return {
|
||||
"items": items,
|
||||
@@ -776,6 +784,13 @@ def get_dashboard() -> Dict[str, Any]:
|
||||
bookmarked_count = conn.execute(
|
||||
"SELECT COUNT(*) FROM announcements WHERE is_bookmarked = 1"
|
||||
).fetchone()[0]
|
||||
profile_row = conn.execute(
|
||||
"SELECT min_match_score FROM user_profile WHERE id = 1"
|
||||
).fetchone()
|
||||
min_score = profile_row["min_match_score"] if profile_row else 70
|
||||
pass_count = conn.execute(
|
||||
"SELECT COUNT(*) FROM match_results WHERE match_score >= ?", (min_score,)
|
||||
).fetchone()[0]
|
||||
|
||||
# 다가오는 일정을 개별 이벤트로 분해
|
||||
upcoming_rows = conn.execute("""
|
||||
@@ -820,6 +835,7 @@ def get_dashboard() -> Dict[str, Any]:
|
||||
return {
|
||||
"active_count": active,
|
||||
"new_match_count": new_matches,
|
||||
"pass_count": pass_count,
|
||||
"bookmarked_count": bookmarked_count,
|
||||
"upcoming_schedules": schedules,
|
||||
"bookmarked": bookmarked_items,
|
||||
|
||||
Reference in New Issue
Block a user