Spaces:
Sleeping
Sleeping
| """ | |
| main.py | |
| FastAPI app สำหรับ Hugging Face Space (Docker) | |
| - ดึงข้อมูล e-GP ของ SUT ครั้งเดียวตอน startup แล้ว cache ไว้ใน memory | |
| - เปิด endpoint ให้ frontend (Lovable) เรียกไปแสดงผล | |
| - มี endpoint /refresh สำหรับสั่งดึงข้อมูลใหม่แบบ manual (ไม่ auto-refresh) | |
| """ | |
| from fastapi import FastAPI, Query | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from contextlib import asynccontextmanager | |
| from typing import Optional | |
| import logging | |
| from scraper import scrape_all | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger("sut-tender-api") | |
| # cache เก็บข้อมูลไว้ใน memory ของ process | |
| CACHE: dict = {"data": [], "last_updated": None, "error": None} | |
| async def lifespan(app: FastAPI): | |
| # ดึงข้อมูลครั้งเดียวตอน startup ตามที่ตกลงกันไว้ | |
| refresh_cache() | |
| yield | |
| app = FastAPI(title="SUT Tender API", lifespan=lifespan) | |
| # เปิด CORS ให้ Lovable (หรือ origin ใดๆ) เรียกได้ | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| def refresh_cache(): | |
| import datetime | |
| try: | |
| logger.info("Scraping SUT e-GP data...") | |
| data = scrape_all() | |
| CACHE["data"] = data | |
| CACHE["last_updated"] = datetime.datetime.utcnow().isoformat() | |
| CACHE["error"] = None | |
| logger.info(f"Done. Got {len(data)} items.") | |
| except Exception as e: | |
| logger.exception("Scrape failed") | |
| CACHE["error"] = str(e) | |
| def root(): | |
| return { | |
| "status": "ok", | |
| "items_cached": len(CACHE["data"]), | |
| "last_updated": CACHE["last_updated"], | |
| "error": CACHE["error"], | |
| } | |
| def get_tenders( | |
| keyword: Optional[str] = Query( | |
| None, | |
| description="กรองคำในชื่อโครงการ รองรับหลายคำคั่นด้วย comma เช่น 'server,คอมพิวเตอร์,computer' (เจอคำใดคำหนึ่งก็ติด)", | |
| ), | |
| limit: int = Query(50, ge=1, le=500), | |
| offset: int = Query(0, ge=0), | |
| ): | |
| items = CACHE["data"] | |
| if keyword: | |
| # แตกคำด้วย comma แล้ว trim whitespace, ตัดคำว่างทิ้ง | |
| keywords = [kw.strip().lower() for kw in keyword.split(",") if kw.strip()] | |
| if keywords: | |
| items = [ | |
| i for i in items | |
| if any(kw in i["title"].lower() for kw in keywords) | |
| ] | |
| total = len(items) | |
| items = items[offset: offset + limit] | |
| return { | |
| "total": total, | |
| "limit": limit, | |
| "offset": offset, | |
| "last_updated": CACHE["last_updated"], | |
| "items": items, | |
| } | |
| def refresh(): | |
| """สั่งดึงข้อมูลใหม่แบบ manual (ไม่ auto-refresh ตามที่ตกลงไว้)""" | |
| refresh_cache() | |
| return {"status": "refreshed", "items_cached": len(CACHE["data"]), "error": CACHE["error"]} | |