수집한 객실·이용 정보가 발행 화면에 연결되지 않던 경로를 보완하고, 숙소 소개와 지역 맛집 표시를 개선한다. - NOL 브라우저 수집 어댑터와 수집·반영 스크립트 추가 - 크롤링 fact 즉시 노출 및 직접 입력·정정값 보호 - 이용안내 항목별 구조화와 기존 표 연결, 원문 UI 비표시 - 군산 한일옥 고정 등록과 지역 맛집 탐색·보강 경로 추가 - 숙소 소개 요약, 히어로 문구, 지역 콘텐츠·목업 표시 개선 검증: 작업 트리 기준 site 타입·린트·빌드 및 안내 렌더링 테스트 통과, PC·모바일 화면 확인. 스테이징 diff 공백 검사 통과. 사용자 요청에 따라 현재 스테이징된 55개 파일만 포함하며 미스테이징 문서·테스트 등은 제외.
143 lines
5.7 KiB
Python
143 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
테스트 place에 야놀자 크롤링으로 넣은 객실 fact/사진을 **수동으로 검수·승인**해서,
|
|
실제 미리보기 렌더러(site_payload.to_site_payload → UnitsSection)에 뜨는지 확인하는
|
|
1회성 스크립트.
|
|
|
|
★ 이것도 운영 파이프라인이 아니다 — 사람이 눈으로 확인하려고 이번 테스트 place 하나에만
|
|
쓰는 수동 검수 도구다. 실제 서비스에서 크롤링 값을 이렇게 자동 승인하면 안 된다
|
|
(fact는 사람이 [맞아요]를 눌러야, 사진은 Vision/사람이 봐야 승인 상태가 된다).
|
|
|
|
사용법 (solution/backend 에서, 가상환경 안):
|
|
PGSSLMODE=disable DB_PASSWORD=... python scripts/verify_yanolja_test_place.py --place-id <UUID>
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import asyncio
|
|
import json
|
|
import os
|
|
import sys
|
|
import uuid
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
os.environ.setdefault("APP_ENV", "local")
|
|
|
|
from sqlalchemy import select # noqa: E402
|
|
|
|
from common.database.db_session_manager import DB_SESSION_MNG # noqa: E402
|
|
from common.database.model.models import places, place_facts, place_photos # noqa: E402
|
|
from common.enums import DBWRType, ErrorType, FactStatus, MediaStatus # noqa: E402
|
|
from common.models.gmodel import UserInfo # noqa: E402
|
|
from common.utils.gtime import GTime # noqa: E402
|
|
from crud.fact_crud import FactCRUD # noqa: E402
|
|
from crud.media_crud import MediaCRUD # noqa: E402
|
|
from crud.place_crud import PlaceCRUD # noqa: E402
|
|
from crud.site_crud import SiteCRUD # noqa: E402
|
|
from crud.job_crud import JobQueue # noqa: E402
|
|
from router.v1.fact.protocol import Req_TransitionFact # noqa: E402
|
|
from services.fact_service import FactService # noqa: E402
|
|
from services.site_service import SiteService # noqa: E402
|
|
|
|
# 검수 전 상태(우리가 이번에 넣은 값들)만 건드린다.
|
|
_PENDING_FACT_STATUSES = (FactStatus.UNVERIFIED.value, FactStatus.PENDING_OWNER.value)
|
|
|
|
|
|
async def _load_place(place_id: str):
|
|
stmt = select(places).where(places.place_id == uuid.UUID(place_id), places.deleted == False) # noqa: E712
|
|
err, row = await DB_SESSION_MNG.execute_lambda(
|
|
places.DBType(), DBWRType.DB_READ.value, lambda s: DB_SESSION_MNG.execute(s, stmt)
|
|
)
|
|
if err != ErrorType.SUCCESS or not row:
|
|
return None
|
|
return row[0]
|
|
|
|
|
|
async def _unit_scope_facts(place_id: str):
|
|
stmt = select(place_facts).where(
|
|
place_facts.place_id == uuid.UUID(place_id),
|
|
place_facts.deleted == False, # noqa: E712
|
|
place_facts.unit_id.is_not(None),
|
|
place_facts.status.in_(_PENDING_FACT_STATUSES),
|
|
)
|
|
err, rows = await DB_SESSION_MNG.execute_lambda(
|
|
place_facts.DBType(), DBWRType.DB_READ.value, lambda s: DB_SESSION_MNG.execute(s, stmt)
|
|
)
|
|
return rows if err == ErrorType.SUCCESS else []
|
|
|
|
|
|
async def _unit_scope_pending_photos(place_id: str):
|
|
stmt = select(place_photos).where(
|
|
place_photos.place_id == uuid.UUID(place_id),
|
|
place_photos.deleted == False, # noqa: E712
|
|
place_photos.unit_id.is_not(None),
|
|
place_photos.status == MediaStatus.PENDING_REVIEW.value,
|
|
)
|
|
err, rows = await DB_SESSION_MNG.execute_lambda(
|
|
place_photos.DBType(), DBWRType.DB_READ.value, lambda s: DB_SESSION_MNG.execute(s, stmt)
|
|
)
|
|
return rows if err == ErrorType.SUCCESS else []
|
|
|
|
|
|
async def run(place_id: str) -> None:
|
|
place = await _load_place(place_id)
|
|
if place is None:
|
|
raise SystemExit(f"place를 찾을 수 없습니다: {place_id}")
|
|
|
|
actor = UserInfo(user_id=str(place.owner_user_id), id="yanolja_test_verify", role=1)
|
|
fact_service = FactService(FactCRUD(), PlaceCRUD())
|
|
media_crud = MediaCRUD()
|
|
|
|
facts = await _unit_scope_facts(place_id)
|
|
print(f"[검수 대상 fact] {len(facts)}건")
|
|
verified = 0
|
|
for f in facts:
|
|
res = await fact_service.transition(
|
|
actor, place_id, str(f.fact_id), Req_TransitionFact(status=FactStatus.VERIFIED)
|
|
)
|
|
if res.result.success:
|
|
verified += 1
|
|
else:
|
|
print(f" ! {f.key}({f.fact_id}) 전이 실패: {res.result.desc}")
|
|
print(f" -> VERIFIED 처리 {verified}/{len(facts)}건")
|
|
|
|
photos = await _unit_scope_pending_photos(place_id)
|
|
print(f"[승인 대상 사진] {len(photos)}건")
|
|
approved = 0
|
|
now = GTime.UTC()
|
|
for p in photos:
|
|
alt_text = p.label or "객실 사진"
|
|
run_err, _rowcount = await DB_SESSION_MNG.execute_lambda_claim(
|
|
place_photos.DBType(),
|
|
lambda s, m=p.media_id, a=alt_text: media_crud.apply_vision(
|
|
s, m, p.label, a, None, MediaStatus.APPROVED.value, now
|
|
),
|
|
)
|
|
if run_err == ErrorType.SUCCESS:
|
|
approved += 1
|
|
print(f" -> APPROVED 처리 {approved}/{len(photos)}건 (alt_text 채움)")
|
|
|
|
site_service = SiteService(SiteCRUD(), PlaceCRUD(), JobQueue())
|
|
payload = await site_service.preview_payload(actor, place_id)
|
|
units = (payload or {}).get("units", [])
|
|
print(f"\n[/preview 재확인] units {len(units)}개")
|
|
for u in units:
|
|
print(f" - {u.get('name')}: fact {len(u.get('facts', []))}개, media {len(u.get('mediaIds', []))}장")
|
|
if not units:
|
|
print(" (여전히 비어 있습니다 — payload 구조나 상태값을 다시 확인하세요)")
|
|
print(json.dumps(payload, ensure_ascii=False, default=str)[:2000])
|
|
|
|
await DB_SESSION_MNG.dispose_all()
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description="테스트 place의 야놀자 객실 데이터를 검수·승인해서 미리보기에 반영")
|
|
parser.add_argument("--place-id", required=True)
|
|
args = parser.parse_args()
|
|
asyncio.run(run(args.place_id))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|