novaaa1011 commited on
Commit
a22bf4f
·
verified ·
1 Parent(s): f498d44

Upload 16 files

Browse files
Files changed (3) hide show
  1. main.py +28 -4
  2. modules/retrieval.py +30 -4
  3. schemas.py +25 -7
main.py CHANGED
@@ -6,6 +6,7 @@ import os
6
  import requests
7
  from fastapi.security import OAuth2PasswordRequestForm
8
  from schemas import SortOption
 
9
 
10
  # Import modules
11
  from schemas import (
@@ -18,7 +19,7 @@ from modules.retrieval import retrieve_context, get_destinations_paginated, get_
18
  from modules.generation import build_rag_prompt, call_llm_api, parse_llm_response
19
  from modules.weather import get_current_weather
20
  from core.config import settings
21
-
22
  import favorites
23
 
24
  # Khởi tạo Vectorizer
@@ -48,6 +49,14 @@ app.add_middleware(
48
  allow_headers=["*"],
49
  )
50
 
 
 
 
 
 
 
 
 
51
  @app.get("/", tags=["Health"])
52
  def root():
53
  """Health check endpoint"""
@@ -93,12 +102,27 @@ app.include_router(favorites.router, prefix="/favorites", tags=["User Favorites"
93
  # ==========================================
94
  @app.get("/destinations", response_model=PaginatedResponse)
95
  async def list_destinations(
96
- filters: HardConstraints = Depends(),
97
- sort_by: SortOption = Query(SortOption.RATING_DESC, description="Sắp xếp theo: Tên (A-Z) hoặc Rating"),
 
 
 
 
 
 
 
98
  page: int = Query(1, ge=1),
99
  limit: int = Query(10, ge=1, le=50)
100
  ):
101
- """Hiển thị danh sách địa điểm theo trang và bộ lọc cơ bản (Không dùng AI)"""
 
 
 
 
 
 
 
 
102
  return get_destinations_paginated(filters, sort_by, page, limit)
103
 
104
  # ==========================================
 
6
  import requests
7
  from fastapi.security import OAuth2PasswordRequestForm
8
  from schemas import SortOption
9
+ from fastapi.responses import JSONResponse
10
 
11
  # Import modules
12
  from schemas import (
 
19
  from modules.generation import build_rag_prompt, call_llm_api, parse_llm_response
20
  from modules.weather import get_current_weather
21
  from core.config import settings
22
+ from typing import List, Optional
23
  import favorites
24
 
25
  # Khởi tạo Vectorizer
 
49
  allow_headers=["*"],
50
  )
51
 
52
+ @app.exception_handler(Exception)
53
+ async def global_exception_handler(request: Request, exc: Exception):
54
+ print(f"🔥 LỖI NGHIÊM TRỌNG (500) TẠI {request.url}: {exc}")
55
+ return JSONResponse(
56
+ status_code=500,
57
+ content={"message": "Internal Server Error", "detail": str(exc)},
58
+ )
59
+
60
  @app.get("/", tags=["Health"])
61
  def root():
62
  """Health check endpoint"""
 
102
  # ==========================================
103
  @app.get("/destinations", response_model=PaginatedResponse)
104
  async def list_destinations(
105
+
106
+ budget_range: Optional[List[str]] = Query(None, description="Lọc theo ngân sách"),
107
+ available_time: Optional[List[str]] = Query(None, description="Lọc theo thời gian"),
108
+ companion_tag: Optional[List[str]] = Query(None, description="Lọc theo người đi cùng"),
109
+ season_tag: Optional[List[str]] = Query(None, description="Lọc theo mùa"),
110
+ location_province: Optional[str] = Query(None, description="Lọc theo tỉnh"),
111
+ # -----------------------
112
+
113
+ sort_by: SortOption = Query(SortOption.RATING_DESC),
114
  page: int = Query(1, ge=1),
115
  limit: int = Query(10, ge=1, le=50)
116
  ):
117
+ # Bước 2: Gom lại thành object HardConstraints để truyền cho hàm xử lý
118
+ filters = HardConstraints(
119
+ budget_range=budget_range,
120
+ available_time=available_time,
121
+ companion_tag=companion_tag,
122
+ season_tag=season_tag,
123
+ location_province=location_province
124
+ )
125
+
126
  return get_destinations_paginated(filters, sort_by, page, limit)
127
 
128
  # ==========================================
modules/retrieval.py CHANGED
@@ -77,10 +77,36 @@ def get_destinations_paginated(filters: HardConstraints, sort_option: SortOption
77
  .limit(limit)
78
 
79
  results = []
80
- for doc in cursor:
81
- doc["id"] = str(doc.get("landmark_id", ""))
82
- results.append(doc)
83
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  return {
85
  "data": results,
86
  "total": total_docs,
 
77
  .limit(limit)
78
 
79
  results = []
80
+ print(f"--- Đang xử lý trang {page} ---")
81
+
82
+ for index, doc in enumerate(cursor):
83
+ try:
84
+ # 1. FIX LỖI OBJECT ID (Quan trọng nhất)
85
+ if "_id" in doc:
86
+ doc["_id"] = str(doc["_id"]) # Chuyển ObjectId thành string ngay lập tức
87
+
88
+ # 2. Xử lý ID
89
+ # Ưu tiên landmark_id, nếu không có thì lấy _id đã string hóa
90
+ doc["id"] = str(doc.get("landmark_id", doc.get("_id", "")))
91
+
92
+ # 3. Xử lý Rating (Chống null)
93
+ if doc.get("overall_rating") is None:
94
+ doc["overall_rating"] = 0.0
95
+
96
+ # 4. Xử lý Image (Chống null)
97
+ if doc.get("image_urls") is None:
98
+ doc["image_urls"] = []
99
+
100
+ results.append(doc)
101
+
102
+ except Exception as e:
103
+ # IN LỖI RA TERMINAL ĐỂ BẠN THẤY
104
+ print(f"❌ LỖI DATA TẠI ITEM #{index} (Landmark ID: {doc.get('landmark_id')}):")
105
+ print(f" Lý do: {e}")
106
+ # continue giúp bỏ qua item lỗi này, không làm sập cả trang
107
+ continue
108
+ # --- KẾT THÚC ĐOẠN CODE SỬA ---
109
+
110
  return {
111
  "data": results,
112
  "total": total_docs,
schemas.py CHANGED
@@ -17,13 +17,31 @@ class WeatherInfo(BaseModel):
17
  icon: str = Field(..., description="Mã icon của OpenWeather")
18
 
19
  class HardConstraints(BaseModel):
20
- # Dùng cho cả filter danh sách và AI
21
- budget_range: Optional[List[str]] = Field(default=None, description="Ngân sách (multi-select)")
22
- available_time: Optional[List[str]] = Field(default=None, description="Thời gian rảnh")
23
- companion_tag: Optional[List[str]] = Field(default=None, description="Bạn đồng hành")
24
- season_tag: Optional[List[str]] = Field(default=None, description="Mùa")
25
- location_province: Optional[str] = Field(default=None, description="Lọc theo tỉnh thành")
26
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  # --- 2. PAGINATION & LIST VIEW SCHEMAS ---
28
  class DestinationSummary(BaseModel):
29
  # Khớp với logic: Backend sẽ gán landmark_id vào field này
 
17
  icon: str = Field(..., description="Mã icon của OpenWeather")
18
 
19
  class HardConstraints(BaseModel):
20
+ budget_range: Optional[List[str]] = Query(
21
+ default=None,
22
+ description="Ngân sách (multi-select), VD: ?budget_range=thấp&budget_range=cao"
23
+ )
24
+
25
+ available_time: Optional[List[str]] = Query(
26
+ default=None,
27
+ description="Thời gian rảnh"
28
+ )
29
+
30
+ companion_tag: Optional[List[str]] = Query(
31
+ default=None,
32
+ description="Bạn đồng hành"
33
+ )
34
+
35
+ season_tag: Optional[List[str]] = Query(
36
+ default=None,
37
+ description="Mùa"
38
+ )
39
+
40
+ location_province: Optional[str] = Query(
41
+ default=None,
42
+ description="Lọc theo tỉnh thành"
43
+ )
44
+
45
  # --- 2. PAGINATION & LIST VIEW SCHEMAS ---
46
  class DestinationSummary(BaseModel):
47
  # Khớp với logic: Backend sẽ gán landmark_id vào field này