Commit Graph

32 Commits

Author SHA1 Message Date
f676116336 fix(realestate): 네이버 매물 dong_code를 dong명에서 유도(cortarNo 부재)→ market_deals 매칭·판정 복구
네이버 article에 cortarNo가 없는 경우 listing.dong_code가 None으로 저장되어
get_market_deals_for가 시세를 못 찾아 모든 매물이 '보류'(판정 불가) 상태였다.
dong명은 항상 정확히 태깅되므로 lawd_code(dong)으로 5자리 시군구코드를 유도해
market_deals와 매칭시킨다.

- internal_router.listings_ingest: dong_code 없으면 batch.dong에서 lawd_code로 보정
- db.upsert_listing: ON CONFLICT UPDATE SET에 dong_code 추가(재유입 시 기존 None행 갱신)
- listing_matcher.run_listing_matching: get_market_deals_for 호출 전 dong_code 없으면
  listing.dong에서 즉시 유도(기존 저장된 None행도 재실행만으로 판정 복구)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
2026-07-10 14:11:51 +09:00
4097c95286 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
2026-07-10 13:56:45 +09:00
b471d2c455 perf(realestate): market_deals 배치 upsert(단일 conn)로 collect 속도 개선 2026-07-10 13:50:35 +09:00
8e28ce9ae5 fix(realestate): listings-ingest async(BackgroundTask)+매칭/알림 타이밍 로그 + notify 홍수방지(cap+baseline) 2026-07-10 11:11:36 +09:00
1fff511752 fix(realestate): compose INTERNAL_API_KEY(배포 401 방지) + 매칭·알림 공유락(중복 텔레그램 방지)
- docker-compose.yml realestate-lab environment에 INTERNAL_API_KEY/NAVER_PAGE_LIMIT 누락 추가
  (verify_internal_key 항상 401 → 워커 계약 전체 마비 방지, sibling image/video/music/insta-lab과 동형)
- pipeline_lock.py 신설: cron _run_listing_pipeline과 워커 listings_ingest가
  run_listing_matching()+notify_new_listings() 임계구역을 공유 threading.Lock으로 직렬화
  (동시 실행 시 동일 매물 중복 텔레그램 발송 방지). 느린 collect는 락 밖 유지.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
2026-07-10 00:50:55 +09:00
04081bef6a refactor(realestate): collect_listings MOLIT 전용화(네이버는 워커 ingest로 이전)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 00:36:53 +09:00
13e3e558af feat(realestate): naver 워커 listings-ingest(파싱→upsert→매칭→알림) 2026-07-10 00:31:42 +09:00
f931c496d8 feat(realestate): 내부 인증 + naver 워커 targets 엔드포인트 + nginx internal 블록
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
2026-07-10 00:27:23 +09:00
008111eff8 feat(realestate): 네이버 cortarNo 10자리 법정동 매핑(NAVER_CORTAR) 2026-07-10 00:23:54 +09:00
cdc309150e feat(realestate): 매물 수집 MOLIT/Naver 진단 계측(collect_log.error) — silent-failure 관측
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
2026-07-09 17:09:59 +09:00
95fadaa8ef fix(realestate): 시세 표본 광역폴백(M1)+area-NULL upsert skip(M3)+matches JSON파싱(M4)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
2026-07-09 14:03:54 +09:00
b4fb3998fe feat(realestate): 매물 라우트 6종+safety-check/budget+3h cron
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
2026-07-09 13:43:34 +09:00
4847626424 feat(realestate): notify_new_listings — 매물 알림 agent-office push 2026-07-09 13:39:19 +09:00
08ac800910 feat(realestate): 매물 수집(국토부 실거래[합법]+네이버 호가[폴백])
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
2026-07-09 13:36:20 +09:00
d752675e9d feat(realestate): 매물 매칭+안전마진(전세가율)/매매 적정성(호가율) 판정
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
2026-07-09 13:33:11 +09:00
c0a50f4ee6 feat(realestate): 매물 DB 4테이블(listings/market_deals/matches/criteria)+헬퍼
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
2026-07-09 13:29:36 +09:00
cd15504f86 feat(realestate): 재무·규제 규칙(전세/매수 예산·토허·LTV·DSR config) 2026-07-09 13:24:00 +09:00
e8998a4098 feat(realestate): 매물 파이프라인 법정동코드(LAWD_CD) 매핑 2026-07-09 13:20:59 +09:00
55c37df703 feat(realestate): wire cleanup + notifier into scheduled flow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:52:00 +09:00
c2939459e7 fix(realestate-notifier): preserve threshold=0 and test sent_ids retry path
- Replace `or 70` fallback with explicit None-check so that
  min_match_score=0 ("notify all matches") is no longer silently
  coerced to 70
- Add test: 200 OK + sent_ids=[] must not mark matches notified
- Add test: threshold=0 correctly pushes low-score matches

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:50:29 +09:00
7aa7ccc6d5 feat(realestate-notifier): push unnotified matches to agent-office
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:46:59 +09:00
d46d2cb30b test(realestate): truncate tables for isolation instead of file delete
Replace os.remove() in conftest autouse fixture with per-table DELETE
to avoid Windows SQLite file-lock PermissionError being swallowed
silently and leaking state across tests. Remove the inline DELETE
workaround from test_profile_api.py that was coupling the test to
internal DB knowledge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:45:03 +09:00
20b51f706c feat(realestate-profile): expose 5tier districts + min_match_score + notify_enabled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:40:51 +09:00
eb04b954a5 refactor(realestate-matcher): integer tier points + clearer legacy path docstring
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:38:09 +09:00
a75ff069df feat(realestate-matcher): 5-tier district weighting + eligibility curve
지역 점수를 35점(광역 10 + 자치구 S/A/B/C/D 티어 0~25)으로 재배분하고,
자격 점수를 25점(첫 자격 15 + 추가당 5, 최대 +10) 곡선으로 변경.
총점 구성: 지역 35 + 유형 10 + 면적 15 + 가격 15 + 자격 25 = 100.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:34:19 +09:00
d39d9f26ac fix(realestate-collector): district regex tolerates missing separator 2026-04-28 08:31:55 +09:00
9dd517e82a feat(realestate-collector): 30-day window + district extraction + completed skip
- Add _extract_district() helper with DISTRICT_PATTERN regex (서울 only)
- collect_all() now passes RCRIT_PBLANC_DE_FROM param (30-day window) to all detail endpoints
- collect_all() skips announcements where compute_status() returns '완료'
- collect_all() stamps district on each parsed announcement before upsert
- upsert_announcement(): add district to INSERT/VALUES/ON CONFLICT UPDATE; data.setdefault('district', None)
- ANNOUNCEMENT_COLUMNS: add 'district' (closes deferred gap from Task 2 review)
- 9 new tests in realestate-lab/tests/test_collector.py (6 unit + 3 integration)
- Full suite: 22 passed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:28:10 +09:00
496e3a6a73 refactor(realestate-db): tuple param + status column in unnotified query
- mark_matches_notified: pass tuple(match_ids) to conn.execute for consistency
- get_unnotified_matches: add a.status to SELECT so notifier/formatter can skip stale '완료' matches
- add regression test: test_get_unnotified_matches_includes_status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:24:03 +09:00
d6547edf0d feat(realestate-db): add notify queue + 90-day grace cleanup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:19:03 +09:00
5749d4d35d feat(realestate-db): add district / notify / 5tier columns with migration
- announcements.district + idx_ann_district 인덱스
- user_profile: preferred_districts(JSON obj), min_match_score(int 70), notify_enabled(bool)
- match_results: notified_at(TEXT)
- _profile_row_to_dict: notify_enabled bool화, preferred_districts dict 역직렬화
- PROFILE_COLUMNS 확장 (3 신규 필드)
- upsert_profile: list|dict 모두 JSON 직렬화

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:13:44 +09:00
2477342272 test(realestate): fix mktemp deprecation + narrow exception in conftest
Replace deprecated mktemp with mkstemp to eliminate TOCTOU race, narrow
OSError catches to PermissionError (Windows SQLite lock intent only), and
add a deferred-import comment for clarity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:11:19 +09:00
62a9009fea test(realestate): add pytest harness with isolated SQLite fixture
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:07:59 +09:00