Commit Graph

338 Commits

Author SHA1 Message Date
1d4354e402 feat(agent-office): YouTubeResearchAgent + 스케줄러 + /youtube/research API
- db.py: youtube_research_jobs 테이블 추가 + CRUD 3종 (add/update/get_latest)
- agents/youtube.py: YouTubeResearchAgent 신규 구현 (on_schedule/on_command/on_approval/_run_research/send_weekly_report)
- agents/__init__.py: YouTubeResearchAgent 등록
- scheduler.py: youtube_research(매일 09:00) + youtube_weekly_report(월 08:00) cron 추가
- main.py: POST /api/agent-office/youtube/research + GET /api/agent-office/youtube/research/status 엔드포인트 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 12:13:12 +09:00
8604c6292d fix(agent-office): get_running_loop + pytrends timeout + UA 수정
- asyncio.get_event_loop() → asyncio.get_running_loop() (python 3.10+ 권장)
- TrendReq에 timeout=(5, 15) 추가 (connect, read timeout)
- User-Agent에서 'bot' 제거: 표준 Chrome UA로 변경

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 12:10:09 +09:00
21666f4372 feat(agent-office): youtube_researcher — YouTube API·pytrends·Billboard 수집
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 12:05:58 +09:00
f83b900320 fix: frontend 서비스에 /data/videos 볼륨 마운트 추가 2026-05-01 12:04:32 +09:00
a7b2fc0d9d chore: FFmpeg 설치 + Nginx /media/videos/ + docker-compose volumes + 환경변수 2026-05-01 12:03:19 +09:00
327d0b4e81 fix(music-lab): VIDEO_DATA_DIR 기본값 통일 + lazy import 정리
- VIDEO_DATA_DIR 기본값을 /app/data/videos로 수정 (기존 /app/data에 videos 서브디렉토리를 중복 붙이던 버그 수정)
- delete_project, export_project의 경로에서 중복된 "videos" 서브디렉토리 제거
- create_project 내부의 get_track_by_id lazy import를 파일 상단 import 블록으로 이동
2026-05-01 12:01:59 +09:00
8e7a3806c5 feat(music-lab): 영상 프로젝트 6개 + 수익화 5개 API 추가
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 11:59:11 +09:00
abf475433b fix(music-lab): xfade offset 누적 오차 수정 + 테스트 보강
- _build_slideshow_cmd: offset 공식을 `duration_per_image * i - xd * i`로 수정 (누적 전환 오차 제거)
- _generate_metadata: genre 빈 문자열일 때 yt_tags에 빈 문자열 삽입 방지
- test: VIDEO_DATA_DIR 패치를 monkeypatch로 교체 (자동 복원 보장)
- test: xfade offset 값 검증 테스트 추가 (29.00, 58.00)
- test: 미사용 import 제거 (pytest, sqlite3)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 11:56:41 +09:00
7336fd090e feat(music-lab): video_producer — FFmpeg 비주얼라이저·슬라이드쇼 + Claude 메타데이터
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 11:49:42 +09:00
62d79b2669 fix(music-lab): revenue avg_rpm 공식 수정 + UNIQUE 제약 + 테스트 보강
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 11:46:00 +09:00
56fbe3fc4b fix(agent-office): realestate 에이전트 텔레그램 명령 등록
AGENT_META + AGENT_COMMAND_MAP에 realestate 누락으로 /realestate 명령 인식 불가.
- /realestate matches → fetch_matches
- /realestate dashboard → dashboard
- /help에 청약 에이전트 항목 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 11:45:45 +09:00
a5495aeaa4 feat(music-lab): video_projects·revenue_records DB 마이그레이션 + CRUD
- init_db()에 video_projects, revenue_records 테이블 추가 (CREATE IF NOT EXISTS)
- video_projects CRUD: create/get/get_all/update_status/delete + get_track_by_id
- revenue_records CRUD: create/get_all/update/delete/get_revenue_dashboard (RPM 자동 계산)
- TDD: tests/test_db_video.py 5개 테스트 모두 PASSED
- pytest.ini 추가 (pythonpath=. 설정)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 11:41:07 +09:00
88b5ea9ce2 chore: .worktrees/ gitignore 추가 2026-05-01 11:36:56 +09:00
54d67f892c docs(spec): music-lab YouTube 수익화 고도화 설계 문서 추가
시장 조사 자동화 + 영상 제작 파이프라인 + 수익화 추적 전체 설계.
Phase 1(영상 제작) → Phase 2(시장 조사) → Phase 3(YouTube API) 로드맵 포함.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 11:14:25 +09:00
8411e2c73e feat(realestate): 결과발표 공고 매칭 점수 보존
run_matching() 대상을 '청약예정/청약중/결과발표'로 확장.
삭제는 '완료' 상태만 — 결과발표 단계의 매칭 기록은 유지.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:32:58 +09:00
86a6b75124 feat(realestate): API 수집 retry 로직 추가 (최대 2회, 지수 백오프)
페이지 요청 실패 시 즉시 중단 대신 1초·2초 대기 후 재시도.
2회 모두 실패 시에만 해당 엔드포인트 수집 종료, 나머지 엔드포인트 계속 진행.
JSON 파싱 오류는 재시도 없이 즉시 skip.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:32:26 +09:00
08a32e4357 feat(realestate): 신혼부부 특공 혼인 기간 검증 추가 (84개월 이내)
marriage_months 미입력 시 기존대로 통과, 85개월 이상이면 신혼부부 특공 자격 제외.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:30:39 +09:00
f6de95afb6 feat(realestate): 소득 기준 검증 추가 (특별공급 자격 판정)
income_level(도시근로자 월평균 대비 %) 필드를 활용하여 특별공급 자격 검증:
- 신혼부부·생애최초: 160% 이하 (맞벌이 민간분양 상한)
- 신생아: 200% 이하 (맞벌이 기준)
- 청년: 140% 이하
- 다자녀·노부모부양: 소득 기준 없음
- 미입력 시 검증 생략 (기존 동작 유지)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:12:21 +09:00
caacb072a2 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>
2026-05-01 08:56:27 +09:00
f80683ce82 docs(claude): realestate-lab + agent-office 청약 타겟팅 흐름 보강
- realestate-lab: 자치구 5티어 매칭 모델 / scheduled_collect 4단계 / 신규 컬럼 / notifier.py / 신규 endpoint
- agent-office: RealestateAgent.on_new_matches / 콜백 라우팅 / 신규 환경변수 / 데일리 cron 폐기
- 매칭 점수 모델: 35/10/15/15/25 = 100점 명시

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 06:58:24 +09:00
71f52e4d59 docs(plan): 청약 타겟팅 프론트엔드 구현 계획
9 task TDD 분할 (단위 테스트 인프라 없음, 빌드+린트+수동 시각 검증):
- Task 1: DEFAULT_PROFILE 확장 + extractTier 헬퍼
- Task 2: DistrictTierEditor (드래그&드롭 + 모바일 read-only)
- Task 3: NotificationSettings (슬라이더 + 토글)
- Task 4: ProfileTab 통합 + handleSave
- Task 5: Subscription.css (5티어 + 드래그 영역 + 토글 + 슬라이더)
- Task 6-8: AnnouncementCard / AnnouncementDetail / MatchesTab district + 5티어 뱃지
- Task 9: CLAUDE.md + 수동 시각 검증 12 시나리오

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 10:47:20 +09:00
756f280bbc docs(spec): 청약 타겟팅 프론트엔드 설계
- DistrictTierEditor: 데스크톱 드래그&드롭 + 모바일 read-only
- NotificationSettings: 임계값 슬라이더 + 알림 토글
- AnnouncementCard/MatchesTab: district + 5티어 뱃지
- AnnouncementDetail: 매칭 분석 섹션 (점수 + reasons + 자격)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 10:40:36 +09:00
a508a5633a Merge branch 'feat/realestate-targeting'
청약 서비스 타겟팅 고도화 — 12 task TDD 구현
- realestate-lab: 자치구 5티어 가중치 매칭, 30일 윈도우 수집, 90일 grace 자동 정리
- agent-office: 신규 매칭 즉시 텔레그램 푸시 + 인라인 키보드 (북마크/공고 보기)
- 단일 SoT: preferred_districts/min_match_score/notify_enabled 프로필 필드
- 데일리 리포트 cron 폐기, 09:00 수집 직후 push로 통합
- 62 tests passing (realestate-lab 44 + agent-office 18)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 09:55:23 +09:00
1d6c1b4329 fix(agent-office): bookmark field name + service_proxy contract + mktemp
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 09:09:05 +09:00
7b3ddd1b19 chore(deploy): wire realestate↔agent-office URLs for push notify
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 09:03:43 +09:00
32e021cfc7 feat(agent-office): drop daily realestate cron + bookmark callback routing
- scheduler.py: remove _run_realestate_schedule() and its 09:15 cron job
- service_proxy.py: add realestate_bookmark_toggle() helper (PATCH bookmark endpoint)
- webhook.py: add _handle_realestate_bookmark() dispatcher before DB-lookup path;
  realestate_bookmark_{id} callbacks are handled inline without a DB entry
- tests/test_realestate_callback.py: 4 new unit tests covering happy path,
  invalid id, proxy error, and regression that approve/reject still uses DB path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 09:01:38 +09:00
3749d79168 feat(agent-office): realestate on_new_matches + /notify endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:57:52 +09:00
0de2d3cf93 feat(agent-office-telegram): realestate match formatter + keyboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:54:34 +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
0fadc774d8 docs(plan): 청약 타겟팅 고도화 구현 계획
12 task TDD 분할:
- realestate-lab: 테스트 셋업 → 스키마 마이그 → 신규 함수 → collector/matcher → profile API → notifier → 흐름 통합
- agent-office: 텔레그램 fmt → on_new_matches + endpoint → cron 폐기 + 콜백 라우팅
- 마지막: docker-compose 환경변수 + 회귀 검증

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 03:45:10 +09:00
eef2e3967e docs(spec): 청약 타겟팅 고도화 설계
- 수집 사전 좁힘(30일 윈도우) + 완료 공고 90일 grace 자동 정리
- 자치구 5티어 가중치 매칭 (S/A/B/C/D)
- realestate-lab → agent-office push 기반 즉시 텔레그램 알림
- 데일리 리포트 cron 폐기, 임계값 통과 신규 매칭만 푸시

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 03:36:38 +09:00
2a8635e9ed refactor: backend→lotto 서비스 리네이밍 + lotto.db 레거시 테이블 스키마 제거
- backend/ → lotto/ 디렉토리 이동
- docker-compose: lotto-backend→lotto, lotto-frontend→frontend
- deploy scripts, nginx, agent-office config 네이밍 일괄 반영
- lotto/app/db.py에서 todos·blog_posts CREATE TABLE 제거 (personal로 이관 완료)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 17:29:13 +09:00
6c46759848 fix(deploy): deploy-nas.sh 전체 런타임 디렉토리에 chown+chmod 일괄 적용
서비스별 개별 처리 대신 $DST 전체에 대해 chown -R + chmod -R 755 수행.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 16:55:19 +09:00
e3d5eaf6f3 refactor: portfolio → personal 리네이밍 + Blog/Todo 통합
- portfolio/ 디렉토리를 personal/로 리네이밍
- lotto-backend의 Blog/Todo 라우트·CRUD를 personal 서비스로 이전
- lotto-backend에서 Blog/Todo 코드 제거 (DB 테이블 스키마는 유지)
- nginx: /api/todos, /api/blog/ 라우팅을 personal로 추가
- docker-compose: portfolio → personal 서비스 변��
- deploy 스크립트: portfolio → personal 반영

데이터 마이그레이션은 배포 후 NAS에서 별도 수행 필요:
1. cp data/portfolio/portfolio.db data/personal/personal.db
2. sqlite3 data/lotto.db ".dump todos" | sqlite3 data/personal/personal.db
3. sqlite3 data/lotto.db ".dump blog_posts" | sqlite3 data/personal/personal.db

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 16:32:55 +09:00
6004bcf66d fix(deployer): git pull 후 파일 소유권을 PUID:PGID로 복원
deployer 컨테이너가 root로 git pull을 실행하면 새 파일이
root:root 소유로 생성되어 다른 컨테이너에서 권한 문제 발생.
pull 직후 chown -R로 원래 소유권(bgg8988:users)을 복원.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 15:05:39 +09:00
a5a9337838 fix(nginx): portfolio 프록시를 변수 기반으로 변경
직접 proxy_pass로 portfolio:8000 참조 시 컨테이너 미실행 상태에서
nginx DNS 해석 실패 → 재시작 루프 발생. resolver + 변수 패턴 적용.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 14:56:21 +09:00
4d6296bce3 docs: CLAUDE.md에 portfolio 서비스 추가 (9개 서비스)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 14:39:05 +09:00