feat(travel-proxy): 앨범 지역 변경 API + 좌표 메타 API + region_map_extra 리팩토링
- PUT /api/travel/albums/{album}/region: 앨범의 지역 변경 (extra 파일 기반)
- PUT /api/travel/regions/{region_id}: 커스텀 지역 이름/좌표 수정 (Phase 2 준비)
- _load_extra/_save_extra 헬퍼 분리, _removes 키로 원본 오버라이드 지원
- regions API: 모든 커스텀 지역 동적 병합 + Point geometry 지원
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ from pydantic import BaseModel
|
||||
from PIL import Image
|
||||
|
||||
from .db import init_db, get_photos_by_region, get_all_albums, set_album_cover, mark_thumb_done
|
||||
from .indexer import sync, _load_region_map_merged
|
||||
from .indexer import sync, _load_region_map_merged, move_album_region
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
@@ -109,6 +109,15 @@ class CoverRequest(BaseModel):
|
||||
filename: str
|
||||
|
||||
|
||||
class RegionRequest(BaseModel):
|
||||
region: str
|
||||
|
||||
|
||||
class RegionMetaRequest(BaseModel):
|
||||
name: str | None = None
|
||||
coordinates: list[float] | None = None # [lng, lat]
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Routes
|
||||
# -----------------------------
|
||||
@@ -120,18 +129,40 @@ def health():
|
||||
@app.get("/api/travel/regions")
|
||||
def regions():
|
||||
geojson = load_regions_geojson()
|
||||
# 미분류 지역이 region_map에 있으면 GeoJSON에도 동적 추가
|
||||
region_map = load_region_map()
|
||||
|
||||
# GeoJSON에 이미 있는 지역 ID 수집
|
||||
existing_ids = {
|
||||
f.get("properties", {}).get("id")
|
||||
for f in geojson.get("features", [])
|
||||
}
|
||||
if "미분류" in region_map and "미분류" not in existing_ids:
|
||||
|
||||
# region_map에 있지만 GeoJSON에 없는 커스텀 지역을 동적 추가
|
||||
# _regions_meta에 좌표가 있으면 Point geometry로, 없으면 null
|
||||
from .indexer import _load_extra
|
||||
extra = _load_extra(THUMB_ROOT)
|
||||
regions_meta = extra.get("_regions_meta", {})
|
||||
|
||||
for region_id in region_map:
|
||||
if region_id in existing_ids:
|
||||
continue
|
||||
meta = regions_meta.get(region_id, {})
|
||||
coords = meta.get("coordinates") # [lng, lat] or None
|
||||
geometry = (
|
||||
{"type": "Point", "coordinates": coords}
|
||||
if coords and len(coords) == 2
|
||||
else None
|
||||
)
|
||||
geojson.setdefault("features", []).append({
|
||||
"type": "Feature",
|
||||
"properties": {"id": "미분류", "name": "미분류"},
|
||||
"geometry": None,
|
||||
"properties": {
|
||||
"id": region_id,
|
||||
"name": meta.get("name", region_id),
|
||||
"custom": True,
|
||||
},
|
||||
"geometry": geometry,
|
||||
})
|
||||
|
||||
return geojson
|
||||
|
||||
|
||||
@@ -216,6 +247,32 @@ def albums_list():
|
||||
return result
|
||||
|
||||
|
||||
@app.put("/api/travel/regions/{region_id}")
|
||||
def update_region_meta(region_id: str, body: RegionMetaRequest):
|
||||
"""커스텀 지역의 이름/좌표 수정 (Phase 2: 지도 핀 표시용)."""
|
||||
from .indexer import _load_extra, _save_extra
|
||||
extra = _load_extra(THUMB_ROOT)
|
||||
meta = extra.setdefault("_regions_meta", {})
|
||||
entry = meta.setdefault(region_id, {})
|
||||
if body.name is not None:
|
||||
entry["name"] = body.name
|
||||
if body.coordinates is not None:
|
||||
if len(body.coordinates) != 2:
|
||||
raise HTTPException(400, "coordinates must be [lng, lat]")
|
||||
entry["coordinates"] = body.coordinates
|
||||
_save_extra(THUMB_ROOT, extra)
|
||||
return {"region_id": region_id, **entry}
|
||||
|
||||
|
||||
@app.put("/api/travel/albums/{album}/region")
|
||||
def set_region(album: str, body: RegionRequest):
|
||||
new_region = body.region.strip()
|
||||
if not new_region:
|
||||
raise HTTPException(400, "Region name cannot be empty")
|
||||
result = move_album_region(THUMB_ROOT, REGION_MAP_PATH, album, new_region)
|
||||
return result
|
||||
|
||||
|
||||
@app.put("/api/travel/albums/{album}/cover")
|
||||
def set_cover(album: str, body: CoverRequest):
|
||||
ok = set_album_cover(album, body.filename)
|
||||
|
||||
Reference in New Issue
Block a user