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:
jtricerolph 2026-07-05 13:47:10 +00:00
parent c9f2dc804c
commit 69c9b16a2b
6 changed files with 5787 additions and 52 deletions

View file

@ -93,6 +93,39 @@ class NewbookRatesClient:
"region": self.region
}
async def get_room_categories(self) -> List[Dict]:
"""
Fetch room categories by grouping the sites_list endpoint.
Each site includes site_category_id and site_category_name; categories
are derived by grouping sites and counting rooms per category.
Returns:
List of dicts with {category_id, category_name, room_count}
"""
payload = self._get_auth_payload()
response = await self.client.post(
self._get_url("sites_list"),
json=payload,
auth=(self.username, self.password)
)
response.raise_for_status()
sites = response.json().get("data") or []
categories: Dict[str, Dict] = {}
for site in sites:
cat_id = str(site.get("site_category_id") or "")
if not cat_id:
continue
entry = categories.setdefault(cat_id, {
"category_id": cat_id,
"category_name": site.get("site_category_name") or f"Category {cat_id}",
"room_count": 0,
})
entry["room_count"] += 1
return sorted(categories.values(), key=lambda c: c["category_name"])
async def get_category_rates(
self,
category_id: str,