Rate parity: expected-markup config, alert writer job, alerts UI + direct-link UI
Parity was read-only — the alerts table had no producer, so the Market
View badge could never fire. Now:
- jobs/check_rate_parity.py: daily 06:45 job comparing own Booking.com
lead-in rate vs cheapest Newbook rate per date, measured against an
EXPECTED markup (we deliberately price Booking.com higher to cover
commission): alert when deviation from newbook*(1+markup%) exceeds the
tolerance. Creates/updates active alerts, auto-resolves dates back in
line, leaves acknowledged dates alone.
- config keys: parity_check_enabled, parity_expected_markup_pct,
parity_tolerance_pct (system_config)
- POST /competitors/parity/check manual trigger; GET /parity now uses the
same markup/tolerance and cheapest-across-categories Newbook rate
- Settings -> Rate Parity tab: markup %, tolerance %, enable toggle,
run-now with result summary
- Market View -> Parity Alerts tab: status-filtered list w/ acknowledge
- Market View -> Hotels: direct-link dropdown per competitor (new PUT
/competitors/hotels/{id}/direct-link) — closes the never-written
direct_hotel_id gap so the matrix direct-rates sub-row can populate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
029e3b379f
commit
493e87b2dc
5 changed files with 595 additions and 12 deletions
|
|
@ -61,6 +61,13 @@ async def run_scheduled_direct_scrape():
|
|||
await loop.run_in_executor(None, run_scrape_all_direct)
|
||||
|
||||
|
||||
async def run_scheduled_parity_check():
|
||||
from jobs.check_rate_parity import run_parity_check
|
||||
import asyncio
|
||||
loop = asyncio.get_event_loop()
|
||||
await loop.run_in_executor(None, run_parity_check)
|
||||
|
||||
|
||||
async def run_scheduled_booking_scrape_async():
|
||||
from jobs.scrape_booking_rates import run_scheduled_booking_scrape
|
||||
import asyncio
|
||||
|
|
@ -116,8 +123,16 @@ def start_scheduler():
|
|||
replace_existing=True,
|
||||
)
|
||||
|
||||
# Rate parity check — daily at 06:45, after Newbook fetch + Booking scrape
|
||||
scheduler.add_job(
|
||||
run_scheduled_parity_check,
|
||||
CronTrigger(hour=6, minute=45),
|
||||
id='parity_check',
|
||||
replace_existing=True,
|
||||
)
|
||||
|
||||
scheduler.start()
|
||||
logger.info(f"Scheduler started: booking scrape at {scrape_hour:02d}:{scrape_minute:02d}, rates fetch at {rates_hour:02d}:{rates_minute:02d}, direct scrape at 06:00")
|
||||
logger.info(f"Scheduler started: booking scrape at {scrape_hour:02d}:{scrape_minute:02d}, rates fetch at {rates_hour:02d}:{rates_minute:02d}, direct scrape at 06:00, parity check at 06:45")
|
||||
|
||||
|
||||
def shutdown_scheduler():
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue