Fix require_cap TypeError, rewrite /analysis/comparison, add room category sync
- require_cap was a Depends-factory but every call site uses it inline; make it an inline checker (fixes 500 on /analysis/hotels, /direct/*) - /analysis/comparison returned a per-date matrix the frontend never read; return per-hotel aggregates (our/their avg, price index) and default to all active competitors so the Market Comparison table works without params - Room categories were never populated (lost in port): add sites_list fetch to the Newbook client, categories list/sync/toggle endpoints, and a Settings card — without included categories every rates sync exits early Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c9f2dc804c
commit
69c9b16a2b
6 changed files with 5787 additions and 52 deletions
|
|
@ -62,6 +62,95 @@ class RateMatrixResponse(BaseModel):
|
|||
date_last_updated: Dict[str, Optional[str]] = {}
|
||||
|
||||
|
||||
# ============================================
|
||||
# ROOM CATEGORIES
|
||||
# ============================================
|
||||
|
||||
class RoomCategory(BaseModel):
|
||||
id: int
|
||||
site_id: str
|
||||
site_name: str
|
||||
room_count: int
|
||||
is_included: bool
|
||||
display_order: int
|
||||
|
||||
|
||||
class CategoryUpdate(BaseModel):
|
||||
is_included: bool
|
||||
|
||||
|
||||
@router.get("/categories", response_model=List[RoomCategory])
|
||||
async def list_categories(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""List Newbook room categories and their include-in-sync status."""
|
||||
result = await db.execute(
|
||||
text("""
|
||||
SELECT id, site_id, site_name, room_count, is_included, display_order
|
||||
FROM newbook_room_categories
|
||||
ORDER BY display_order, site_name
|
||||
""")
|
||||
)
|
||||
return [dict(r) for r in result.mappings().all()]
|
||||
|
||||
|
||||
@router.post("/categories/sync")
|
||||
async def sync_categories(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Fetch room categories from the Newbook API (sites_list) and upsert them.
|
||||
New categories default to included; existing include flags are preserved.
|
||||
"""
|
||||
from services.newbook_rates_client import NewbookRatesClient
|
||||
|
||||
client = await NewbookRatesClient.from_db(db)
|
||||
try:
|
||||
async with client:
|
||||
categories = await client.get_room_categories()
|
||||
except Exception as e:
|
||||
logger.error(f"Category sync failed: {e}")
|
||||
raise HTTPException(status_code=502, detail=f"Newbook category fetch failed: {str(e)}")
|
||||
|
||||
if not categories:
|
||||
raise HTTPException(status_code=502, detail="Newbook returned no room categories — check credentials in Settings")
|
||||
|
||||
for cat in categories:
|
||||
await db.execute(
|
||||
text("""
|
||||
INSERT INTO newbook_room_categories (site_id, site_name, room_count, fetched_at)
|
||||
VALUES (:site_id, :site_name, :room_count, NOW())
|
||||
ON CONFLICT (site_id) DO UPDATE SET
|
||||
site_name = EXCLUDED.site_name,
|
||||
room_count = EXCLUDED.room_count,
|
||||
fetched_at = NOW()
|
||||
"""),
|
||||
{"site_id": cat["category_id"], "site_name": cat["category_name"], "room_count": cat["room_count"]}
|
||||
)
|
||||
await db.commit()
|
||||
return {"status": "success", "count": len(categories)}
|
||||
|
||||
|
||||
@router.patch("/categories/{category_id}")
|
||||
async def update_category(
|
||||
category_id: int,
|
||||
body: CategoryUpdate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""Toggle whether a category is included in rate syncs."""
|
||||
result = await db.execute(
|
||||
text("UPDATE newbook_room_categories SET is_included = :inc WHERE id = :id RETURNING id"),
|
||||
{"inc": body.is_included, "id": category_id}
|
||||
)
|
||||
if not result.fetchone():
|
||||
raise HTTPException(status_code=404, detail="Category not found")
|
||||
await db.commit()
|
||||
return {"status": "success"}
|
||||
|
||||
|
||||
# ============================================
|
||||
# RATE MATRIX ENDPOINT
|
||||
# ============================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue