o2o-site-AEO/solution/backend/router/v1/place/protocol.py
Mina Choi 11d30bb3d1 [chore] solution,admin,ontology: 코드 주석을 한 줄로 — 히스토리 주석 삭제
여러 줄 주석이 설명보다 경위(예전·실측·지적)를 적고 있어 읽는 사람이 결론을 찾기 어려웠다.

- ts·tsx·js·mjs·css·py 478개: 여러 줄 주석은 첫 문장 한 줄로, 과거형·날짜 문장은 삭제
- 주석 위치는 TypeScript 파서·파이썬 tokenize/ast 로 찾는다 — 문자열 안의 # · /* 는 건드리지 않는다
- eslint·ts·noqa·type: ignore 같은 지시 주석은 그대로 둔다

파이썬 275개 정리 전후 AST 동일, TS 298개 주석 뺀 토큰 동일(빈 JSX 주석 10곳만 차이).
site·frontend·admin tsc, site vitest 105 passed

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:05:19 +09:00

230 lines
6.9 KiB
Python

import uuid
from datetime import datetime
from decimal import Decimal
from typing import Optional
from pydantic import ConfigDict
from common.enums import ExternalPlaceSource, JobStatus, LinkChannel, PlaceCategory, PlaceStatus, SourceType
from common.models.gmodel import Res_PageProtocol, Res_WebPacketProtocol, WebPacketProtocol
# 라우터 폴더마다 protocol.py 를 두고 Req_/Res_ 를 정의한다 (protocol 규약).
class PlaceProtocol(WebPacketProtocol):
pass
class Req_CreatePlace(PlaceProtocol):
# 상호명 하나로 시작한다.
name: str = ""
category: PlaceCategory = PlaceCategory.LODGING
# 주인은 받지 않는다 — 토큰이 정한다(place_service.create_place).
class Req_VerifyPlace(PlaceProtocol):
"""카카오 로컬 조회 결과를 사업장에 박제한다(동일 업소 확정)."""
source: ExternalPlaceSource = ExternalPlaceSource.NAVER
external_place_id: str = ""
# 외부 장소 DB 가 함께 준 업체 홈페이지 URL.
place_url: Optional[str] = None
road_address: Optional[str] = None
address: Optional[str] = None
phone: Optional[str] = None
latitude: Optional[Decimal] = None
longitude: Optional[Decimal] = None
region_code: Optional[str] = None
# 외부 장소 DB 의 분류 문자열(후보의 category_name).
category_name: Optional[str] = None
class Req_VerifyPlaceByUrl(PlaceProtocol):
"""네이버 플레이스 URL 하나로 동일 업소를 확정한다."""
url: str = ""
# 같은 가게를 이미 갖고 있을 때 그 사업장으로 이어붙일지.
reuse_existing: bool = True
class Req_UpdatePlace(PlaceProtocol):
# 주인은 못 바꾼다(위 Req_CreatePlace 주석).
name: Optional[str] = None
status: Optional[PlaceStatus] = None
# 미니 블로그 승인 메일 수신 주소.
notify_email: Optional[str] = None
class Req_CreateUnit(PlaceProtocol):
name: str = ""
sort_order: int = 0
class Req_CreateLink(PlaceProtocol):
channel: LinkChannel = LinkChannel.ETC
url: str = ""
title: Optional[str] = None
discovered_by: SourceType = SourceType.OWNER
class PlaceData(WebPacketProtocol):
model_config = ConfigDict(from_attributes=True)
place_id: uuid.UUID
name: str
category: PlaceCategory
status: PlaceStatus
owner_user_id: Optional[uuid.UUID] = None
external_source: Optional[ExternalPlaceSource] = None
external_place_id: Optional[str] = None
road_address: Optional[str] = None
address: Optional[str] = None
phone: Optional[str] = None
latitude: Optional[Decimal] = None
longitude: Optional[Decimal] = None
region_code: Optional[str] = None
verified_at: Optional[datetime] = None
content_updated_at: Optional[datetime] = None # 노출값 변경 시각 — 개별 재빌드 대상 판별
notify_email: Optional[str] = None # 미니 블로그 승인 메일 수신 주소.
created_at: Optional[datetime] = None
class UnitData(WebPacketProtocol):
model_config = ConfigDict(from_attributes=True)
unit_id: uuid.UUID
place_id: uuid.UUID
name: str
sort_order: int = 0
class LinkData(WebPacketProtocol):
model_config = ConfigDict(from_attributes=True)
link_id: uuid.UUID
place_id: uuid.UUID
channel: LinkChannel
url: str
title: Optional[str] = None
discovered_by: SourceType
discovered_at: Optional[datetime] = None
confirmed_at: Optional[datetime] = None # NULL = 크롤링 대상 아님
class Res_PlaceList(Res_PageProtocol):
places: list[PlaceData] = []
class Res_Place(Res_WebPacketProtocol):
place: Optional[PlaceData] = None
class Res_UnitList(Res_WebPacketProtocol):
units: list[UnitData] = []
class Res_Unit(Res_WebPacketProtocol):
unit: Optional[UnitData] = None
class Res_LinkList(Res_WebPacketProtocol):
links: list[LinkData] = []
confirmed: int = 0 # 크롤링 가능한 링크 수
class Res_Link(Res_WebPacketProtocol):
link: Optional[LinkData] = None
class Req_StartCollect(PlaceProtocol):
"""수집 시작."""
# 확정된 채널 URL 만 크롤링한다.
link_ids: list[uuid.UUID] = []
# 이미 확보한 fact 를 다시 긁을지.
force: bool = False
# 상호·주소로 공개 채널 URL 을 Perplexity 에서 추가 탐색할지.
discover_channels: bool = False
class Res_StartCollect(Res_WebPacketProtocol):
"""수집 잡 적재 결과."""
job_id: Optional[uuid.UUID] = None
status: Optional[JobStatus] = None
created: bool = True
confirmed_links: int = 0
class Req_StartVision(PlaceProtocol):
"""사진 분석 시작."""
# 이미 분석된 사진도 다시 태울지.
force: bool = False
class Res_StartVision(Res_WebPacketProtocol):
job_id: Optional[uuid.UUID] = None
status: Optional[JobStatus] = None
created: bool = True
pending_media: int = 0 # 분석 대상 사진 수
# 동일 업소 후보 (UI 가 사람에게 고르게 한다)
class PlaceCandidate(WebPacketProtocol):
"""외부 장소 DB 에서 찾은 후보 1건."""
external_place_id: Optional[str] = None # 네이버는 안 준다
name: str = ""
road_address: Optional[str] = None
address: Optional[str] = None
phone: Optional[str] = None # 네이버는 안 준다
latitude: Optional[Decimal] = None
longitude: Optional[Decimal] = None
category_name: Optional[str] = None
place_url: Optional[str] = None # 업체 홈페이지 — 확정 시 공식 채널로 등록된다
naver_place_url: Optional[str] = None # 자동 발견한 네이버 플레이스.
class Res_VerifyCandidates(Res_WebPacketProtocol):
"""동일 업소 후보 목록."""
source: Optional[ExternalPlaceSource] = None
outcome: str = "" # matched | ambiguous | no_candidate
reason: Optional[str] = None # 판정 근거(name_exact / name_duplicate / name_no_exact …)
auto_selectable: bool = False # True 면 UI 가 '이거 맞나요?' 한 번만 물어도 된다
candidates: list[PlaceCandidate] = []
# 공개 상호명 검색 (랜딩 첫 화면)
class PlaceSearchItem(WebPacketProtocol):
"""공개 검색 결과 1건."""
name: str = ""
road_address: Optional[str] = None
category_name: Optional[str] = None # 외부 DB 의 분류 문자열(예: "숙박>펜션")
# 추정 업종.
category: Optional[PlaceCategory] = None
# 자동으로 찾은 네이버 플레이스 주소.
naver_place_url: Optional[str] = None
class Res_PlaceSearch(Res_WebPacketProtocol):
"""상호명 공개 검색 결과."""
source: Optional[ExternalPlaceSource] = None
items: list[PlaceSearchItem] = []
class Req_StartCopy(PlaceProtocol):
"""소개문·FAQ 생성 시작."""
resume: bool = False
class Res_StartCopy(Res_WebPacketProtocol):
job_id: Optional[uuid.UUID] = None
status: Optional[JobStatus] = None
created: bool = True
grounded_facts: int = 0 # 근거로 쓸 수 있는 확인된 fact 수