File size: 2,115 Bytes
649703e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
from pydantic import BaseModel
from datetime import date
from typing import Optional
from enum import Enum


class Cabin(str, Enum):
    economy = "economy"
    first = "first"


class TripSearchRequest(BaseModel):
    origin: str                          # e.g. "JAX" or "GNV"
    destinations: list[str]              # e.g. ["HNL", "LAX", "JFK"]
    earliest_departure: date
    latest_return: date
    party_size: int = 2
    trip_duration_nights: int = 4
    preferred_cabin: Cabin = Cabin.economy
    ihg_brands: list[str] = ["Kimpton", "InterContinental", "Hotel Indigo"]


class FlightOption(BaseModel):
    destination: str
    outbound_date: date
    return_date: date
    fare_class: str                      # e.g. "L", "U", "Z"
    base_price: float                    # price for one ticket
    companion_taxes: float               # taxes the companion pays (~$80)
    eligible_for_companion_cert: bool
    cabin: Cabin


class HotelOption(BaseModel):
    destination: str
    property_name: str
    brand: str
    check_in: date
    check_out: date
    points_per_night: int
    total_points: int                    # for nights_paid only
    nights_paid: int
    nights_free: int                     # from 4th-night-free benefit
    cash_rate: Optional[float] = None   # nightly cash rate if available


class RestaurantOption(BaseModel):
    city: str
    name: str
    cuisine: str
    reservation_date: date
    party_size: int
    resy_credit_eligible: bool
    global_dining_access: bool


class TripResult(BaseModel):
    destination: str
    flight: FlightOption
    hotel: Optional[HotelOption] = None
    restaurant: Optional[RestaurantOption] = None
    total_cash_out_of_pocket: float
    total_points_required: int
    benefits_captured: list[str]
    score: float                         # 0–100, higher = better value


class SearchProgress(BaseModel):
    step: str                            # "delta" | "ihg" | "resy" | "done"
    message: str
    progress: int                        # 0–100


class SearchResponse(BaseModel):
    search_id: str
    results: list[TripResult]