Spaces:
Running
Running
| """ | |
| backfill_booking_statuses.py | |
| ββββββββββββββββββββββββββββ | |
| One-time script to fix all existing bookings whose status should have | |
| automatically advanced but didn't because the transition job didn't exist yet. | |
| Rules applied | |
| βββββββββββββ | |
| CONFIRMED β ACTIVE_STAY check-in passed but checkout has NOT yet passed | |
| CONFIRMED β COMPLETED both check-in AND checkout have passed | |
| ACTIVE_STAY β COMPLETED checkout has passed | |
| Run from the AIDA project root: | |
| python scripts/backfill_booking_statuses.py | |
| Set MONGO_URI environment variable (or edit the fallback below) before running. | |
| """ | |
| import asyncio | |
| import os | |
| from datetime import datetime | |
| from motor.motor_asyncio import AsyncIOMotorClient | |
| MONGO_URI = os.getenv("MONGO_URI", "mongodb://localhost:27017") | |
| DB_NAME = os.getenv("MONGO_DB_NAME", "lojiz") | |
| STATUS_CONFIRMED = "Confirmed" | |
| STATUS_ACTIVE_STAY = "ActiveStay" | |
| STATUS_COMPLETED = "Completed" | |
| def _parse_dt(booking: dict, date_field: str, time_field: str, default_hour: int) -> datetime: | |
| raw = booking.get(date_field) | |
| if isinstance(raw, datetime): | |
| base = raw.replace(hour=0, minute=0, second=0, microsecond=0) | |
| elif isinstance(raw, str): | |
| try: | |
| base = datetime.fromisoformat(raw[:10]) | |
| except ValueError: | |
| return datetime.max | |
| else: | |
| return datetime.max | |
| time_str = booking.get(time_field) | |
| if time_str: | |
| try: | |
| h, m = (int(x) for x in time_str.strip().split(":")) | |
| return base.replace(hour=h, minute=m) | |
| except Exception: | |
| pass | |
| return base.replace(hour=default_hour, minute=0) | |
| async def run(): | |
| client = AsyncIOMotorClient(MONGO_URI) | |
| db = client[DB_NAME] | |
| now = datetime.utcnow() | |
| counts = {"confirmed_to_active": 0, "confirmed_to_completed": 0, "active_to_completed": 0, "skipped": 0} | |
| print(f"[{now.isoformat()}] Starting backfill β connecting to {DB_NAME}...") | |
| # ββ Pass 1: Fix CONFIRMED bookings βββββββββββββββββββββββββββββββββββββββ | |
| confirmed = await db["bookings"].find({"status": STATUS_CONFIRMED}).to_list(length=None) | |
| print(f" Found {len(confirmed)} CONFIRMED bookings to evaluate.") | |
| for b in confirmed: | |
| checkin_dt = _parse_dt(b, "check_in_date", "estimated_arrival_time", default_hour=14) | |
| checkout_dt = _parse_dt(b, "check_out_date", "estimated_departure_time", default_hour=11) | |
| if now < checkin_dt: | |
| counts["skipped"] += 1 | |
| continue | |
| if now >= checkout_dt: | |
| new_status = STATUS_COMPLETED | |
| counts["confirmed_to_completed"] += 1 | |
| else: | |
| new_status = STATUS_ACTIVE_STAY | |
| counts["confirmed_to_active"] += 1 | |
| await db["bookings"].update_one( | |
| {"_id": b["_id"]}, | |
| {"$set": {"status": new_status, "updated_at": now}}, | |
| ) | |
| print(f" Booking {b['_id']}: CONFIRMED β {new_status} " | |
| f"(check_in={checkin_dt.isoformat()}, check_out={checkout_dt.isoformat()})") | |
| # ββ Pass 2: Fix ACTIVE_STAY bookings βββββββββββββββββββββββββββββββββββββ | |
| active = await db["bookings"].find({"status": STATUS_ACTIVE_STAY}).to_list(length=None) | |
| print(f" Found {len(active)} ACTIVE_STAY bookings to evaluate.") | |
| for b in active: | |
| checkout_dt = _parse_dt(b, "check_out_date", "estimated_departure_time", default_hour=11) | |
| if now >= checkout_dt: | |
| await db["bookings"].update_one( | |
| {"_id": b["_id"]}, | |
| {"$set": {"status": STATUS_COMPLETED, "updated_at": now}}, | |
| ) | |
| counts["active_to_completed"] += 1 | |
| print(f" Booking {b['_id']}: ACTIVE_STAY β COMPLETED " | |
| f"(check_out={checkout_dt.isoformat()})") | |
| else: | |
| counts["skipped"] += 1 | |
| # ββ Summary βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("\nββ Backfill complete ββββββββββββββββββββββββββββββββββββββββββ") | |
| print(f" CONFIRMED β ACTIVE_STAY : {counts['confirmed_to_active']}") | |
| print(f" CONFIRMED β COMPLETED : {counts['confirmed_to_completed']}") | |
| print(f" ACTIVE_STAY β COMPLETED : {counts['active_to_completed']}") | |
| print(f" Skipped (not yet due) : {counts['skipped']}") | |
| total = counts["confirmed_to_active"] + counts["confirmed_to_completed"] + counts["active_to_completed"] | |
| print(f" Total updated : {total}") | |
| print("βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ") | |
| client.close() | |
| if __name__ == "__main__": | |
| asyncio.run(run()) | |