File size: 4,525 Bytes
2e50ccd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ab345fa
 
 
 
 
 
 
 
2e50ccd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8c371f4
 
2e50ccd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c090f53
2e50ccd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d6f7eeb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
"""Pydantic models for API request/response contracts."""

from __future__ import annotations

from datetime import date, datetime
from enum import Enum
from typing import Optional

from pydantic import BaseModel, Field


class CabinClass(str, Enum):
    economy = "economy"
    premium_economy = "premium_economy"
    business = "business"
    first = "first"


class TripType(str, Enum):
    one_way = "one_way"
    round_trip = "round_trip"
    multi_city = "multi_city"


class SortBy(str, Enum):
    best = "best"
    cheapest = "cheapest"
    fastest = "fastest"


# --- Airport ---

class AirportInfo(BaseModel):
    iata: str
    name: str
    city_name: str
    country: str
    country_code: str
    continent: str
    latitude: float
    longitude: float
    timezone: str
    hub_score: float = 0.0
    route_count: int = 0


# --- Flight segment ---

class FlightSegment(BaseModel):
    airline_code: str
    airline_name: str
    flight_number: str
    aircraft: str
    origin: str
    origin_city: str
    destination: str
    destination_city: str
    departure: datetime
    arrival: datetime
    duration_minutes: int
    # Amenities
    legroom_inches: int = 31
    has_wifi: bool = False
    wifi_type: Optional[str] = None       # "Free Wi-Fi", "Wi-Fi for a fee"
    has_power: bool = False
    has_usb: bool = False
    has_video: bool = False
    video_type: Optional[str] = None      # "On-demand video", "Live TV", "Seatback screen"


# --- Flight offer (may have multiple segments) ---

class FlightOffer(BaseModel):
    id: str
    segments: list[FlightSegment]
    total_duration_minutes: int
    stops: int
    price_usd: float
    cabin_class: CabinClass
    origin: str
    destination: str
    departure: datetime
    arrival: datetime
    emissions_kg: int = 0
    is_best: bool = False


# --- Search request ---

class SearchLeg(BaseModel):
    origin: str = Field(..., min_length=3, max_length=3, description="IATA code")
    destination: str = Field(..., min_length=3, max_length=3, description="IATA code")
    date: date


class Passengers(BaseModel):
    adults: int = Field(1, ge=1, le=9)
    children: int = Field(0, ge=0, le=9)
    infants: int = Field(0, ge=0, le=4)

    @property
    def total(self) -> int:
        return self.adults + self.children + self.infants


class Filters(BaseModel):
    max_stops: Optional[int] = None
    max_price: Optional[float] = None
    max_duration_minutes: Optional[int] = None
    airlines: Optional[list[str]] = None  # IATA codes to include
    departure_time_min: Optional[str] = None  # "06:00"
    departure_time_max: Optional[str] = None  # "18:00"


class SearchRequest(BaseModel):
    trip_type: TripType = TripType.round_trip
    legs: list[SearchLeg] = Field(..., min_length=1, max_length=6)
    passengers: Passengers = Passengers()
    cabin_class: CabinClass = CabinClass.economy
    filters: Filters = Filters()
    sort_by: SortBy = SortBy.best


class SearchResponse(BaseModel):
    outbound_flights: list[FlightOffer]
    return_flights: list[FlightOffer] = []
    search_id: str
    origin: str
    destination: str
    same_airline_discount: float = 1.0


# --- Calendar ---

class CalendarDay(BaseModel):
    date: date
    cheapest_price: Optional[float] = None


class CalendarResponse(BaseModel):
    origin: str
    destination: str
    year: int
    month: int
    days: list[CalendarDay]


# --- Autocomplete ---

class AutocompleteResult(BaseModel):
    iata: str
    name: str
    city_name: str
    country: str
    display_name: str
    hub_score: float = 0.0


# --- Booking ---

class PassengerInfo(BaseModel):
    first_name: str
    last_name: str
    email: str
    phone: str


class PaymentInfo(BaseModel):
    card_number: str
    expiry_date: str  # MM/YY
    cvv: str
    cardholder_name: str


class BookingFlightInfo(BaseModel):
    id: str
    origin: str
    destination: str
    departure: datetime
    arrival: datetime
    total_duration_minutes: int
    stops: int
    price_usd: float
    cabin_class: CabinClass
    segments: list[FlightSegment]


class BookingRequest(BaseModel):
    passenger: PassengerInfo
    payment: PaymentInfo
    outbound_flight: BookingFlightInfo
    return_flight: Optional[BookingFlightInfo] = None


class BookingResponse(BaseModel):
    booking_id: str
    status: str
    booked_at: str
    passenger: PassengerInfo
    payment_summary: dict
    outbound_flight: BookingFlightInfo
    return_flight: Optional[BookingFlightInfo] = None
    total_price: float