"""naver-fetch 워커 내부 계약: targets 조회 + listings ingest.""" import os import logging from typing import List, Dict, Any from fastapi import APIRouter, Depends from pydantic import BaseModel from .auth import verify_internal_key from .db import get_listing_criteria, upsert_listing from .lawd_codes import naver_cortar from .listing_collector import _parse_naver_article from .listing_matcher import run_listing_matching from .notifier import notify_new_listings from .pipeline_lock import match_notify_lock logger = logging.getLogger("realestate-lab") router = APIRouter() NAVER_PAGE_LIMIT = int(os.getenv("NAVER_PAGE_LIMIT", "2")) @router.get("/api/internal/realestate/targets", dependencies=[Depends(verify_internal_key)]) def listing_targets(): crit = get_listing_criteria() dongs = [] for d in crit.get("dongs", []): code = naver_cortar(d) if not code: logger.warning("naver cortarNo 매핑 없음 — 대상 제외: %s", d) continue dongs.append({"dong": d, "cortar_no": code}) return {"dongs": dongs, "deal_types": crit.get("deal_types", []), "page_limit": NAVER_PAGE_LIMIT} class _Batch(BaseModel): dong: str articles: List[Dict[str, Any]] = [] class ListingsIngest(BaseModel): fetched_at: str | None = None batches: List[_Batch] = [] @router.post("/api/internal/realestate/listings-ingest", dependencies=[Depends(verify_internal_key)]) def listings_ingest(body: ListingsIngest): received = new = 0 for batch in body.batches: for raw in batch.articles: try: d = _parse_naver_article(raw) if not d.get("article_no"): continue d["dong"] = batch.dong _, is_new = upsert_listing(d) received += 1 if is_new: new += 1 except Exception as e: logger.warning("네이버 ingest 파싱 실패: %s", e) matched = 0 if received: with match_notify_lock: run_listing_matching() noti = notify_new_listings() matched = int(noti.get("sent", 0)) if isinstance(noti, dict) else 0 return {"received": received, "new": new, "matched": matched}