File size: 4,010 Bytes
54b590a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | # 檔名:recommend_api.py
# 功能:
# - 提供 /recommend API
# - 內部呼叫 recommender.run_recommend_model() 來產生 clothe.json
# - 自動把每次收到的 request 存成 json 檔,放在 saved_requests/
# - 自動把每次回傳的 clothe_json 存成 json 檔,放在 saved_clothes/
# - ✅ 自動判斷身形,印出身形,並把結果塞進 report 給推薦系統用
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional, Dict, Any
from pathlib import Path
import json
import time
from recommender import run_recommend_model, ClotheJSON
from body_type_classifier import classify_male_body_type, classify_female_body_type
app = FastAPI(title="Recommendation Service")
BASE_DIR = Path(__file__).resolve().parent
LOG_REQ_DIR = BASE_DIR / "saved_requests"
LOG_REQ_DIR.mkdir(exist_ok=True)
LOG_RES_DIR = BASE_DIR / "saved_clothes"
LOG_RES_DIR.mkdir(exist_ok=True)
class WeatherInfo(BaseModel):
condition: str
temperature: float
humidity: Optional[float] = None
class RecommendRequest(BaseModel):
user_id: Optional[str] = None
report: dict
weather: WeatherInfo
DEFAULT_GENDER = "male"
def extract_gender(report: Dict[str, Any]) -> Optional[str]:
gender = (
report.get("gender") or
report.get("sex") or
report.get("Gender") or
report.get("user_gender")
)
if isinstance(gender, str):
g = gender.lower()
if g in ("male", "m", "boy", "man", "男", "男性"):
return "male"
if g in ("female", "f", "girl", "woman", "女", "女性"):
return "female"
if DEFAULT_GENDER is not None:
print(f"[BodyType] report 中沒有 gender 欄位,暫時使用 DEFAULT_GENDER={DEFAULT_GENDER}")
return DEFAULT_GENDER
return None
def extract_body_measurements(report: Dict[str, Any]) -> Optional[Dict[str, float]]:
bm = report.get("body_measurements")
if isinstance(bm, dict):
return bm
return None
def attach_body_type(report: Dict[str, Any]) -> None:
body_measurements = extract_body_measurements(report)
gender = extract_gender(report)
if not body_measurements or not gender:
print(
f"[BodyType] 無法判斷:缺少 body_measurements 或 gender,"
f"gender={gender}, has_body={bool(body_measurements)}"
)
return
try:
if gender == "male":
body_type = classify_male_body_type(body_measurements)
else:
body_type = classify_female_body_type(body_measurements)
print(f"[BodyType] gender={gender} → body_type={body_type}")
report["body_type"] = body_type
report["body_gender"] = gender
except Exception as e:
print(f"[BodyType] 判斷身形時發生錯誤: {e}")
@app.post("/recommend", response_model=ClotheJSON)
def recommend(req: RecommendRequest) -> ClotheJSON:
weather_dict = req.weather.dict()
timestamp = int(time.time())
user_part = req.user_id if req.user_id else "anonymous"
base_name = f"{user_part}_{timestamp}"
report_dict: Dict[str, Any] = dict(req.report)
attach_body_type(report_dict)
req_path = LOG_REQ_DIR / f"request_{base_name}.json"
with req_path.open("w", encoding="utf-8") as f:
json.dump(
{
"user_id": req.user_id,
"report": report_dict,
"weather": weather_dict,
},
f,
ensure_ascii=False,
indent=2,
)
clothe_json = run_recommend_model(report_dict, weather_dict)
res_path = LOG_RES_DIR / f"clothe_{base_name}.json"
with res_path.open("w", encoding="utf-8") as f:
json.dump(
clothe_json,
f,
ensure_ascii=False,
indent=2,
)
return clothe_json
|