Restore guestline-monitor features: room stock/occupancy, rate history, room stats

- dates endpoint now returns total_avail, benchmark rate (tier-resolved),
  min-stay nights, max_rooms (summed per-room stock) and friendly labels
- rooms endpoint groups by room with stock, occupancy, best/bench rate and
  nested rate plans; injects known-but-absent rooms; honours room_order
- new /history/{stay_date} endpoint: per room/rate series, benchmark mode,
  or overall availability + cheapest rate per scrape run
- new /stats endpoint: per-room stock + windowed avg rate / current occ /
  est. final occ (look-back window so past dates give completed bookings)
- DirectRates page: occupancy pills, avail x/stock, Room Stats tab,
  history chart modal (plotly), expandable rate plans with history links
- fix discovery trigger crashing (run_until_complete inside running loop)
- fix migration script reading wrong column name (engine_profile)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-06 06:50:37 +00:00
parent 74ae94671b
commit 20d939de00
3 changed files with 908 additions and 173 deletions

View file

@ -10,6 +10,7 @@ import sys
from datetime import datetime, timezone
import psycopg2
import psycopg2.extras
ARCHIVE_DIR = os.environ.get(
"GUESTLINE_ARCHIVE",
@ -37,7 +38,7 @@ def migrate():
src = sqlite3.connect(CONFIG_DB)
src.row_factory = dict_row
hotels = src.execute(
"""SELECT id, name, profile, params, room_labels, rate_labels,
"""SELECT id, name, engine_profile, params, room_labels, rate_labels,
room_order, benchmark_room, benchmark_rate,
tier_base_room, tier_offsets, scrape_enabled, last_scraped_at
FROM hotels ORDER BY id"""
@ -65,7 +66,7 @@ def migrate():
RETURNING id""",
(
h["name"],
h["profile"],
h["engine_profile"],
params,
h["room_labels"] or "{}",
h["rate_labels"] or "{}",
@ -163,5 +164,4 @@ def migrate():
if __name__ == "__main__":
import psycopg2.extras
migrate()