Commit graph

63 commits

Author SHA1 Message Date
b712196262 Scraper: process-wide lock, one scrape at a time
Concurrent manual scrapes were interleaving (two Chromium sessions on one
LXC) causing the page timeouts behind partial results. SCRAPE_LOCK guards
run_manual_scrape and process_queue; the trigger endpoint returns 409 when
busy, and the frontend keeps the job queued and retries after 30s.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 16:14:55 +00:00
bf9425ee7e Market View: honest staleness per cell after partial scrapes
- matrix response gains last_scraped per date (max scraped_at across all
  hotels), so column headers show the newest scrape touching the date even
  when the visible hotels were on a failed page
- cells >1h older than the column's latest scrape render italic with *
- every cell tooltip now includes the datestamp the price was scraped

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 15:46:59 +00:00
218b2f45f6 Scraper: don't flag not_listed from partial scrapes
A page-load timeout was logged and skipped, so a scrape could 'succeed'
with only page 1 of results — and the not_listed flagging then marked
every page-2 hotel absent, suppressing their last known rates.

- ScraperResult now tracks pages_requested/pages_ok
- scrape_date skips not_listed flagging when pages failed or when the
  scrape saw <60% of the date's 7-day coverage baseline; scraped rates
  are still saved, unseen hotels keep last known rate + scrape time

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 15:29:06 +00:00
349236cd7e Rate Analysis: fix hotel detail 500 + timeline 422, match frontend shapes; signed % delta badge
- /analysis/hotel/{id}: lead_bucket alias in ORDER BY CASE broke Postgres; replaced
  bucketed curve with per-days_ahead curve and reshaped response to the frontend
  HotelAnalysis interface (strategy/advance_curve/dow_breakdown/sold_out_pattern)
- /analysis/hotel/{id}/timeline: accept ?date= (was rate_date, 422) and return
  flat TimelineEntry array
- /analysis/hotels: alias to hotel_id/hotel_name/date_count for the selector
- strategy pcts default 0 (frontend calls .toFixed), added peak_months
- Market View badge now shows +/-% vs our rate instead of 100-index

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 15:01:57 +00:00
ba6c000903 Flag hotels absent from successful scrapes as 'not_listed' + widen booking_com_id
Location-search results aren't a fixed hotel set — a sold-out hotel drops
out and its last 'available' rate would remain the latest row for that
date, reading as a live price and skewing market averages. On each
successful per-date scrape, insert a NULL-rate 'not_listed' row for every
active hotel missing from the results (skipped if the parse found nothing,
which indicates scraper fault not absence). Failed/blocked scrapes write
nothing, so genuinely-stale data remains distinguishable by scraped_at.

Also: fix the DOW analysis to pick latest-then-filter so a not_listed
latest row drops the date instead of resurfacing an older rate, and widen
booking_com_id to VARCHAR(255) (some Booking slugs exceed 50 chars).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 14:44:02 +00:00
75c2adf803 Fix Booking.com scrape saves — widen room_type to TEXT + rollback per failure
Scraped room descriptions exceed VARCHAR(100) (StringDataRightTruncation),
and the failed insert poisoned the transaction so every subsequent save in
the batch died with InFailedSqlTransaction — scrapes reported success with
0 rows saved. Widen the column (with ALTER for existing tables) and roll
back after a failed save.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 14:32:20 +00:00
f51e1dcb76 Port occupancy sync + category ordering from the forecasting version
- Bookability showed no availability because newbook_occupancy_report_data
  was never populated: add reports_occupancy client method and
  sync_occupancy job, run before rates in both Sync Now and the daily
  schedule (single fast API call)
- Category order: default display_order to the Newbook category id on
  sync (was 0 → alphabetical), preserve manual order on re-sync, extend
  PATCH to accept display_order, add up/down reorder arrows in Settings

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 14:18:03 +00:00
c924d793e8 Fix scheduled Newbook sync — await the coroutine instead of run_in_executor
run_fetch_current_rates is async; run_in_executor called it in a thread
and discarded the coroutine, so the daily scheduled sync silently did
nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 14:09:40 +00:00
33d466ed73 Fix newbook_current_rates column names + rollback on failed date
- schema defined gross_rate/net_rate but every query (fetch job,
  bookability, competitors) uses rate_gross/rate_net — rename the
  columns, with an idempotent DO-block migration for existing tables
- roll back the session when a date fails so one bad statement no
  longer poisons the whole sync run (every subsequent write was dying
  with 'current transaction is aborted')

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 14:07:01 +00:00
4977768645 Fix sites_list category field names — category_id/category_name, not site_category_*
The rates Newbook instance returns categories as category_id/category_name
on each site; also skip inactive categories.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 14:01:07 +00:00
69c9b16a2b 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>
2026-07-05 13:47:10 +00:00
d94b8feee6 Add GET/POST /competitors/config/system endpoints for Settings page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-05 12:30:29 +00:00
e05054172f Add Rate Monitor app — Booking.com + direct booking engine competitor rates
Combines Booking.com Playwright scraper (from forecasting), direct booking
engine scraper (ported from laptop-archive/guestline-monitor), and Newbook
own-hotel rates into one focused tool. Four views: Bookability, Market View
(with price index badges + direct rate sub-rows), Direct Rates (per-competitor
room breakdown, min-stay flags, hotel config/discovery), Rate Analysis
(advance purchase curve, DOW chart, rate timeline, comparison table).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-05 12:06:30 +00:00