from fastapi import FastAPI, HTTPException from typing import List, Dict, Optional import json import requests from pydantic import BaseModel app = FastAPI(title="Vietnam Geographic API", description="API để truy xuất thông tin về các đơn vị hành chính của Việt Nam") # Mô hình Pydantic cho dữ liệu class Ward(BaseModel): name: str class District(BaseModel): name: str wards: List[Ward] class City(BaseModel): name: str districts: List[District] # Tải dữ liệu từ URL thay vì file local def load_geographic_data(): try: # URL dữ liệu từ Hugging Face url = "https://huggingface.co/spaces/pmshoanghot/api_tinh_thanh/resolve/main/vietnam-provinces.json" response = requests.get(url) # Kiểm tra nếu request thành công if response.status_code == 200: return response.json() else: raise HTTPException(status_code=500, detail=f"Không thể tải dữ liệu từ URL: Mã lỗi {response.status_code}") except requests.RequestException as e: raise HTTPException(status_code=500, detail=f"Lỗi khi kết nối đến URL: {str(e)}") except json.JSONDecodeError: raise HTTPException(status_code=500, detail="File JSON không hợp lệ") # Tải dữ liệu khi khởi động ứng dụng geographic_data = load_geographic_data() # Endpoint gốc @app.get("/") async def root(): return { "message": "Chào mừng đến với Vietnam Geographic API. Sử dụng /api/list để lấy danh sách tất cả các tỉnh thành."} # Lấy danh sách tất cả các tỉnh thành @app.get("/api/list", response_model=List[Dict[str, str]]) async def get_cities(): return [{"name": city["name"]} for city in geographic_data] # Lấy danh sách quận/huyện của một tỉnh/thành phố cụ thể @app.get("/api/city/{city_name}/districts", response_model=List[Dict[str, str]]) async def get_districts(city_name: str): for city in geographic_data: if city["name"] == city_name: return [{"name": district["name"]} for district in city["districts"]] raise HTTPException(status_code=404, detail=f"Không tìm thấy tỉnh/thành phố '{city_name}'") # Lấy danh sách phường/xã của một quận/huyện cụ thể trong một tỉnh/thành phố @app.get("/api/city/{city_name}/district/{district_name}/wards", response_model=List[Dict[str, str]]) async def get_wards(city_name: str, district_name: str): for city in geographic_data: if city["name"] == city_name: for district in city["districts"]: if district["name"] == district_name: return [{"name": ward["name"]} for ward in district["wards"]] raise HTTPException(status_code=404, detail=f"Không tìm thấy quận/huyện '{district_name}' trong tỉnh/thành phố '{city_name}'") raise HTTPException(status_code=404, detail=f"Không tìm thấy tỉnh/thành phố '{city_name}'") # Lấy thông tin chi tiết về một tỉnh/thành phố cụ thể @app.get("/api/city/{city_name}", response_model=City) async def get_city_detail(city_name: str): for city in geographic_data: if city["name"] == city_name: return city raise HTTPException(status_code=404, detail=f"Không tìm thấy tỉnh/thành phố '{city_name}'") # Chức năng tìm kiếm (tìm theo tên một phần) @app.get("/api/search", response_model=List[Dict[str, str]]) async def search(q: str, type: Optional[str] = "city"): results = [] if type == "city": results = [{"name": city["name"]} for city in geographic_data if q.lower() in city["name"].lower()] elif type == "district": for city in geographic_data: for district in city["districts"]: if q.lower() in district["name"].lower(): results.append({ "name": district["name"], "city": city["name"] }) elif type == "ward": for city in geographic_data: for district in city["districts"]: for ward in district["wards"]: if q.lower() in ward["name"].lower(): results.append({ "name": ward["name"], "district": district["name"], "city": city["name"] }) return results if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)