Add configurable 30-day NewBook rate rescrape on 2/4/6/12h intervals
Intraday rescrape jobs are distributed evenly between the main nightly run (05:20) and cover only the next 30 days — lightweight complement to the full 720-day nightly sweep. Interval is configurable from the Newbook tab in Settings and takes effect immediately without a container restart. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7d0b7d2d3b
commit
078cb47b16
3 changed files with 238 additions and 1 deletions
|
|
@ -781,6 +781,90 @@ async def get_occupancy_history(
|
|||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# RESCRAPE INTERVAL CONFIG
|
||||
# ============================================
|
||||
|
||||
def _rescrape_times_from_config(interval_hours: int, time_str: str) -> list:
|
||||
"""Compute extra rescrape fire times (HH:MM strings) from interval and base time."""
|
||||
try:
|
||||
parts = time_str.split(':')
|
||||
base_hour, base_minute = int(parts[0]), int(parts[1])
|
||||
except (ValueError, IndexError):
|
||||
base_hour, base_minute = 5, 20
|
||||
if interval_hours not in (2, 4, 6, 12):
|
||||
return []
|
||||
return [
|
||||
f"{(base_hour + offset) % 24:02d}:{base_minute:02d}"
|
||||
for offset in range(interval_hours, 24, interval_hours)
|
||||
]
|
||||
|
||||
|
||||
@router.get("/config/rescrape-schedule")
|
||||
async def get_rescrape_schedule(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""Return current 30-day rescrape interval config and computed fire times."""
|
||||
result = await db.execute(
|
||||
text("""
|
||||
SELECT config_key, config_value FROM system_config
|
||||
WHERE config_key IN ('newbook_rescrape_interval_hours', 'sync_newbook_current_rates_time')
|
||||
""")
|
||||
)
|
||||
cfg = {row.config_key: row.config_value for row in result.fetchall()}
|
||||
try:
|
||||
interval_hours = int(cfg.get('newbook_rescrape_interval_hours') or '0')
|
||||
except ValueError:
|
||||
interval_hours = 0
|
||||
time_str = cfg.get('sync_newbook_current_rates_time') or '05:20'
|
||||
return {
|
||||
"interval_hours": interval_hours,
|
||||
"base_time": time_str,
|
||||
"rescrape_times": _rescrape_times_from_config(interval_hours, time_str),
|
||||
}
|
||||
|
||||
|
||||
class RescrapeIntervalBody(BaseModel):
|
||||
interval_hours: int
|
||||
|
||||
|
||||
@router.post("/config/rescrape-interval")
|
||||
async def set_rescrape_interval(
|
||||
body: RescrapeIntervalBody,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""Save 30-day rescrape interval and live-reload the scheduler jobs."""
|
||||
if body.interval_hours not in (0, 2, 4, 6, 12):
|
||||
raise HTTPException(status_code=400, detail="interval_hours must be 0, 2, 4, 6, or 12")
|
||||
|
||||
await db.execute(
|
||||
text("""
|
||||
INSERT INTO system_config (config_key, config_value)
|
||||
VALUES ('newbook_rescrape_interval_hours', :val)
|
||||
ON CONFLICT (config_key) DO UPDATE SET config_value = EXCLUDED.config_value
|
||||
"""),
|
||||
{"val": str(body.interval_hours)}
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
from scheduler import apply_newbook_rescrape_schedule
|
||||
apply_newbook_rescrape_schedule()
|
||||
|
||||
time_result = await db.execute(
|
||||
text("SELECT config_value FROM system_config WHERE config_key = 'sync_newbook_current_rates_time'")
|
||||
)
|
||||
time_row = time_result.fetchone()
|
||||
time_str = (time_row.config_value if time_row and time_row.config_value else None) or '05:20'
|
||||
return {
|
||||
"status": "saved",
|
||||
"interval_hours": body.interval_hours,
|
||||
"base_time": time_str,
|
||||
"rescrape_times": _rescrape_times_from_config(body.interval_hours, time_str),
|
||||
}
|
||||
|
||||
|
||||
@router.post("/refresh-date/{rate_date}")
|
||||
async def refresh_single_date(
|
||||
rate_date: str,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue