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>
This commit is contained in:
commit
e05054172f
50 changed files with 11860 additions and 0 deletions
80
backend/main.py
Normal file
80
backend/main.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
"""
|
||||
Rate Monitor — FastAPI Backend
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[logging.StreamHandler(sys.stdout)]
|
||||
)
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from database import DATABASE_URL
|
||||
from api import bookability, competitors, analysis, direct
|
||||
from scheduler import start_scheduler, shutdown_scheduler
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
try:
|
||||
import os, psycopg2
|
||||
schema_path = os.path.join(os.path.dirname(__file__), 'schema.sql')
|
||||
if os.path.exists(schema_path):
|
||||
with open(schema_path) as f:
|
||||
sql = f.read()
|
||||
conn = psycopg2.connect(DATABASE_URL)
|
||||
try:
|
||||
conn.autocommit = True
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(sql)
|
||||
logging.getLogger(__name__).info("Schema applied successfully")
|
||||
finally:
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
logging.getLogger(__name__).warning(f"Schema init failed: {e}")
|
||||
|
||||
try:
|
||||
from services.booking_scraper import cleanup_stale_batches
|
||||
from database import SyncSessionLocal
|
||||
db = SyncSessionLocal()
|
||||
try:
|
||||
cleanup_stale_batches(db, max_age_minutes=10)
|
||||
finally:
|
||||
db.close()
|
||||
except Exception as e:
|
||||
logging.getLogger(__name__).warning(f"Stale batch cleanup failed: {e}")
|
||||
|
||||
start_scheduler()
|
||||
yield
|
||||
shutdown_scheduler()
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
title="Rate Monitor API",
|
||||
description="Rate monitoring, competitor scraping, and bookability service",
|
||||
version="1.0.0",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
app.include_router(bookability.router, prefix="/bookability", tags=["Bookability"])
|
||||
app.include_router(competitors.router, prefix="/competitors", tags=["Competitors"])
|
||||
app.include_router(analysis.router, prefix="/analysis", tags=["Rate Analysis"])
|
||||
app.include_router(direct.router, prefix="/direct", tags=["Direct Rates"])
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {"status": "healthy", "service": "rate-monitor-api"}
|
||||
Loading…
Add table
Add a link
Reference in a new issue