bookalimo 1.0.1__py3-none-any.whl → 1.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bookalimo/client.py +45 -22
- bookalimo/integrations/google_places/client_async.py +56 -102
- bookalimo/integrations/google_places/client_sync.py +56 -100
- bookalimo/integrations/google_places/common.py +290 -12
- bookalimo/integrations/google_places/resolve_airport.py +148 -119
- bookalimo/integrations/google_places/transports.py +14 -7
- bookalimo/logging.py +103 -0
- bookalimo/schemas/__init__.py +121 -35
- bookalimo/schemas/base.py +74 -14
- bookalimo/schemas/places/__init__.py +3 -1
- bookalimo/schemas/places/common.py +1 -1
- bookalimo/schemas/places/field_mask.py +0 -9
- bookalimo/schemas/places/google.py +165 -10
- bookalimo/schemas/requests.py +214 -0
- bookalimo/schemas/responses.py +196 -0
- bookalimo/schemas/{booking.py → shared.py} +55 -218
- bookalimo/services/pricing.py +9 -129
- bookalimo/services/reservations.py +10 -100
- bookalimo/transport/auth.py +2 -2
- bookalimo/transport/httpx_async.py +41 -125
- bookalimo/transport/httpx_sync.py +30 -109
- bookalimo/transport/utils.py +204 -3
- bookalimo-1.0.2.dist-info/METADATA +245 -0
- bookalimo-1.0.2.dist-info/RECORD +40 -0
- bookalimo-1.0.1.dist-info/METADATA +0 -370
- bookalimo-1.0.1.dist-info/RECORD +0 -38
- {bookalimo-1.0.1.dist-info → bookalimo-1.0.2.dist-info}/WHEEL +0 -0
- {bookalimo-1.0.1.dist-info → bookalimo-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {bookalimo-1.0.1.dist-info → bookalimo-1.0.2.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
|
-
|
3
|
-
|
2
|
+
Shared base models for Book-A-Limo API data structures.
|
3
|
+
Contains field definitions without serialization opinions -
|
4
|
+
request/response variants inherit from these and set appropriate serialization.
|
4
5
|
"""
|
5
6
|
|
6
7
|
import warnings
|
@@ -14,7 +15,7 @@ import us
|
|
14
15
|
from pydantic import Field, model_validator
|
15
16
|
from typing_extensions import Self
|
16
17
|
|
17
|
-
from .base import
|
18
|
+
from .base import SharedModel
|
18
19
|
|
19
20
|
|
20
21
|
@lru_cache(maxsize=1)
|
@@ -28,15 +29,16 @@ def _load_iata_index() -> tuple[dict[str, Any], dict[str, list[str]]]:
|
|
28
29
|
return data, by_country
|
29
30
|
|
30
31
|
|
32
|
+
# Enums (no serialization issues)
|
31
33
|
class RateType(Enum):
|
32
34
|
"""Rate types for reservations."""
|
33
35
|
|
34
|
-
P2P = 0 # Point-to-Point
|
35
|
-
HOURLY = 1 # Hourly
|
36
|
+
P2P = 0 # Point-to-Point
|
37
|
+
HOURLY = 1 # Hourly
|
36
38
|
DAILY = 2 # Daily
|
37
39
|
TOUR = 3 # Tour
|
38
40
|
ROUND_TRIP = 4 # Round Trip
|
39
|
-
RT_HALF = 5 #
|
41
|
+
RT_HALF = 5 # Round Trip Half Day
|
40
42
|
|
41
43
|
|
42
44
|
class LocationType(Enum):
|
@@ -76,20 +78,17 @@ class ReservationStatus(Enum):
|
|
76
78
|
|
77
79
|
|
78
80
|
class CardHolderType(Enum):
|
79
|
-
"""
|
80
|
-
Credit card holder types (API Documentation Unclear).
|
81
|
-
TODO: Update when API documentation is clarified by the API author.
|
82
|
-
Best guess based on typical credit card processing:
|
83
|
-
"""
|
81
|
+
"""Credit card holder types."""
|
84
82
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
83
|
+
CORPORATE = 0
|
84
|
+
AGENCY = 1
|
85
|
+
THIRD_PARTY = 2
|
86
|
+
SAME_AS_PASSENGER = 3
|
89
87
|
|
90
88
|
|
91
|
-
|
92
|
-
|
89
|
+
# Shared Data Models
|
90
|
+
class CityBase(SharedModel):
|
91
|
+
"""Base city information."""
|
93
92
|
|
94
93
|
city_name: str
|
95
94
|
country_code: str = Field(..., description="ISO 3166-1 alpha-2 country code")
|
@@ -130,15 +129,18 @@ class City(ApiModel):
|
|
130
129
|
)
|
131
130
|
|
132
131
|
|
133
|
-
class
|
134
|
-
"""
|
135
|
-
Address information.
|
136
|
-
"""
|
132
|
+
class AddressBase(SharedModel):
|
133
|
+
"""Base address information."""
|
137
134
|
|
138
135
|
google_geocode: Optional[dict[str, Any]] = Field(
|
139
|
-
default=None,
|
136
|
+
default=None,
|
137
|
+
description=(
|
138
|
+
"Raw Google Geocoding API result (recommended). "
|
139
|
+
"Common mistake is to use the response object instead of the result object. "
|
140
|
+
"You must use geocode_response['results'][0] to get the result object."
|
141
|
+
),
|
140
142
|
)
|
141
|
-
city: Optional[
|
143
|
+
city: Optional["CityBase"] = Field(
|
142
144
|
default=None, description="Use only if google_geocode not available"
|
143
145
|
)
|
144
146
|
district: Optional[str] = Field(default=None, description="e.g., Manhattan")
|
@@ -179,8 +181,8 @@ class Address(ApiModel):
|
|
179
181
|
return self
|
180
182
|
|
181
183
|
|
182
|
-
class
|
183
|
-
"""
|
184
|
+
class AirportBase(SharedModel):
|
185
|
+
"""Base airport information."""
|
184
186
|
|
185
187
|
iata_code: str = Field(..., description="3-letter IATA code, e.g., JFK")
|
186
188
|
country_code: Optional[str] = Field(default=None, description="ISO 3166-1 alpha-2")
|
@@ -195,7 +197,7 @@ class Airport(ApiModel):
|
|
195
197
|
)
|
196
198
|
flight_number: Optional[str] = Field(default=None, description="e.g., UA1234")
|
197
199
|
terminal: Optional[str] = Field(default=None, description="e.g., 7")
|
198
|
-
arriving_from_city: Optional[
|
200
|
+
arriving_from_city: Optional["CityBase"] = None
|
199
201
|
meet_greet: Optional[int] = Field(
|
200
202
|
default=None,
|
201
203
|
description="Meet & greet option ID. Leave empty on price request to see options.",
|
@@ -226,12 +228,12 @@ class Airport(ApiModel):
|
|
226
228
|
return self
|
227
229
|
|
228
230
|
|
229
|
-
class
|
230
|
-
"""
|
231
|
+
class LocationBase(SharedModel):
|
232
|
+
"""Base location (address or airport)."""
|
231
233
|
|
232
234
|
type: LocationType
|
233
|
-
address: Optional[
|
234
|
-
airport: Optional[
|
235
|
+
address: Optional["AddressBase"] = None
|
236
|
+
airport: Optional["AirportBase"] = None
|
235
237
|
|
236
238
|
@model_validator(mode="after")
|
237
239
|
def validate_location(self) -> Self:
|
@@ -244,15 +246,15 @@ class Location(ApiModel):
|
|
244
246
|
return self
|
245
247
|
|
246
248
|
|
247
|
-
class
|
248
|
-
"""
|
249
|
+
class StopBase(SharedModel):
|
250
|
+
"""Base stop information."""
|
249
251
|
|
250
252
|
description: str = Field(..., description="Address, place name, or comment")
|
251
253
|
is_en_route: bool = Field(..., description="True if stop is en-route")
|
252
254
|
|
253
255
|
|
254
|
-
class
|
255
|
-
"""
|
256
|
+
class AccountBase(SharedModel):
|
257
|
+
"""Base travel agency or corporate account info."""
|
256
258
|
|
257
259
|
id: str = Field(..., description="TA or corporate account number")
|
258
260
|
department: Optional[str] = None
|
@@ -262,8 +264,8 @@ class Account(ApiModel):
|
|
262
264
|
booker_phone: Optional[str] = Field(default=None, description="E164 format")
|
263
265
|
|
264
266
|
|
265
|
-
class
|
266
|
-
"""
|
267
|
+
class PassengerBase(SharedModel):
|
268
|
+
"""Base passenger information."""
|
267
269
|
|
268
270
|
first_name: str
|
269
271
|
last_name: str
|
@@ -271,15 +273,15 @@ class Passenger(ApiModel):
|
|
271
273
|
phone: str = Field(..., description="E164 format recommended")
|
272
274
|
|
273
275
|
|
274
|
-
class
|
275
|
-
"""
|
276
|
+
class RewardBase(SharedModel):
|
277
|
+
"""Base reward account information."""
|
276
278
|
|
277
279
|
type: RewardType
|
278
280
|
value: str = Field(..., description="Reward account number")
|
279
281
|
|
280
282
|
|
281
|
-
class
|
282
|
-
"""
|
283
|
+
class CreditCardBase(SharedModel):
|
284
|
+
"""Base credit card information."""
|
283
285
|
|
284
286
|
number: str
|
285
287
|
expiration: str = Field(..., description="MM/YY format")
|
@@ -288,12 +290,12 @@ class CreditCard(ApiModel):
|
|
288
290
|
zip: Optional[str] = None
|
289
291
|
holder_type: Optional[CardHolderType] = Field(
|
290
292
|
default=None,
|
291
|
-
description="Card holder type
|
293
|
+
description="Card holder type",
|
292
294
|
)
|
293
295
|
|
294
296
|
|
295
|
-
class
|
296
|
-
"""
|
297
|
+
class BreakdownItemBase(SharedModel):
|
298
|
+
"""Base price breakdown item."""
|
297
299
|
|
298
300
|
name: str
|
299
301
|
value: float
|
@@ -302,28 +304,28 @@ class BreakdownItem(ApiModel):
|
|
302
304
|
)
|
303
305
|
|
304
306
|
|
305
|
-
class
|
306
|
-
"""
|
307
|
+
class MeetGreetAdditionalBase(SharedModel):
|
308
|
+
"""Base additional meet & greet charges."""
|
307
309
|
|
308
310
|
name: str
|
309
311
|
price: float
|
310
312
|
|
311
313
|
|
312
|
-
class
|
313
|
-
"""
|
314
|
+
class MeetGreetBase(SharedModel):
|
315
|
+
"""Base meet & greet option."""
|
314
316
|
|
315
317
|
id: int
|
316
318
|
name: str
|
317
319
|
base_price: float
|
318
320
|
instructions: str
|
319
|
-
additional: list[
|
321
|
+
additional: list["MeetGreetAdditionalBase"]
|
320
322
|
total_price: float
|
321
323
|
fees: float
|
322
324
|
reservation_price: float
|
323
325
|
|
324
326
|
|
325
|
-
class
|
326
|
-
"""
|
327
|
+
class PriceBase(SharedModel):
|
328
|
+
"""Base car class pricing information."""
|
327
329
|
|
328
330
|
car_class: str
|
329
331
|
car_description: str
|
@@ -335,11 +337,11 @@ class Price(ApiModel):
|
|
335
337
|
image_256: str = Field(alias="image256")
|
336
338
|
image_512: str = Field(alias="image512")
|
337
339
|
default_meet_greet: Optional[int] = None
|
338
|
-
meet_greets: list[
|
340
|
+
meet_greets: list["MeetGreetBase"] = Field(default_factory=list)
|
339
341
|
|
340
342
|
|
341
|
-
class
|
342
|
-
"""
|
343
|
+
class ReservationBase(SharedModel):
|
344
|
+
"""Base reservation information."""
|
343
345
|
|
344
346
|
confirmation_number: str
|
345
347
|
is_archive: bool
|
@@ -353,168 +355,3 @@ class Reservation(ApiModel):
|
|
353
355
|
dropoff: str
|
354
356
|
car_class: str
|
355
357
|
status: Optional[ReservationStatus] = None
|
356
|
-
|
357
|
-
|
358
|
-
class EditableReservationRequest(ApiModel):
|
359
|
-
"""
|
360
|
-
Editable reservation for modifications.
|
361
|
-
|
362
|
-
Note: API documentation inconsistency - credit_card marked as required in model
|
363
|
-
but omitted in edit examples. Making it optional as edit requests may not need it.
|
364
|
-
TODO: Clarify with API author when credit_card is actually required.
|
365
|
-
"""
|
366
|
-
|
367
|
-
confirmation: str
|
368
|
-
is_cancel_request: bool = False
|
369
|
-
rate_type: Optional[RateType] = None
|
370
|
-
pickup_date: Optional[str] = Field(default=None, description="MM/dd/yyyy format")
|
371
|
-
pickup_time: Optional[str] = Field(default=None, description="hh:mm tt format")
|
372
|
-
stops: Optional[list[Stop]] = None
|
373
|
-
credit_card: Optional[CreditCard] = Field(
|
374
|
-
default=None,
|
375
|
-
description="Conditionally required - unclear from API docs when exactly",
|
376
|
-
)
|
377
|
-
passengers: Optional[int] = None
|
378
|
-
luggage: Optional[int] = None
|
379
|
-
pets: Optional[int] = None
|
380
|
-
car_seats: Optional[int] = None
|
381
|
-
boosters: Optional[int] = None
|
382
|
-
infants: Optional[int] = None
|
383
|
-
other: Optional[str] = Field(default=None, description="Other changes not listed")
|
384
|
-
|
385
|
-
|
386
|
-
# Request/Response Models
|
387
|
-
|
388
|
-
|
389
|
-
class PriceRequest(ApiModel):
|
390
|
-
"""Request for getting prices."""
|
391
|
-
|
392
|
-
rate_type: RateType
|
393
|
-
date_time: str = Field(..., description="MM/dd/yyyy hh:mm tt format")
|
394
|
-
pickup: Location
|
395
|
-
dropoff: Location
|
396
|
-
hours: Optional[int] = Field(default=None, description="For hourly rate_type only")
|
397
|
-
passengers: int
|
398
|
-
luggage: int
|
399
|
-
stops: Optional[list[Stop]] = None
|
400
|
-
account: Optional[Account] = Field(
|
401
|
-
default=None, description="TAs must provide for commission"
|
402
|
-
)
|
403
|
-
passenger: Optional[Passenger] = None
|
404
|
-
rewards: Optional[list[Reward]] = None
|
405
|
-
car_class_code: Optional[str] = Field(
|
406
|
-
default=None, description="e.g., 'SD' for specific car class"
|
407
|
-
)
|
408
|
-
pets: Optional[int] = None
|
409
|
-
car_seats: Optional[int] = None
|
410
|
-
boosters: Optional[int] = None
|
411
|
-
infants: Optional[int] = None
|
412
|
-
customer_comment: Optional[str] = None
|
413
|
-
|
414
|
-
|
415
|
-
class PriceResponse(ApiModel):
|
416
|
-
"""Response from get prices."""
|
417
|
-
|
418
|
-
token: str
|
419
|
-
prices: list[Price]
|
420
|
-
|
421
|
-
|
422
|
-
class DetailsRequest(ApiModel):
|
423
|
-
"""Request for setting reservation details."""
|
424
|
-
|
425
|
-
token: str
|
426
|
-
car_class_code: Optional[str] = None
|
427
|
-
pickup: Optional[Location] = None
|
428
|
-
dropoff: Optional[Location] = None
|
429
|
-
stops: Optional[list[Stop]] = None
|
430
|
-
account: Optional[Account] = None
|
431
|
-
passenger: Optional[Passenger] = None
|
432
|
-
rewards: Optional[list[Reward]] = None
|
433
|
-
pets: Optional[int] = None
|
434
|
-
car_seats: Optional[int] = None
|
435
|
-
boosters: Optional[int] = None
|
436
|
-
infants: Optional[int] = None
|
437
|
-
customer_comment: Optional[str] = None
|
438
|
-
ta_fee: Optional[float] = Field(
|
439
|
-
default=None, description="For Travel Agencies - additional fee in USD"
|
440
|
-
)
|
441
|
-
|
442
|
-
|
443
|
-
class DetailsResponse(ApiModel):
|
444
|
-
"""Response from set details."""
|
445
|
-
|
446
|
-
price: float
|
447
|
-
breakdown: list[BreakdownItem]
|
448
|
-
|
449
|
-
|
450
|
-
class BookRequest(ApiModel):
|
451
|
-
"""Request for booking reservation."""
|
452
|
-
|
453
|
-
token: str
|
454
|
-
promo: Optional[str] = None
|
455
|
-
method: Optional[str] = Field(
|
456
|
-
default=None, description="'charge' for charge accounts"
|
457
|
-
)
|
458
|
-
credit_card: Optional[CreditCard] = None
|
459
|
-
|
460
|
-
@model_validator(mode="after")
|
461
|
-
def validate_book_request(self) -> Self:
|
462
|
-
"""Validate that either method or credit_card is provided."""
|
463
|
-
if not self.method and not self.credit_card:
|
464
|
-
raise ValueError("Either method='charge' or credit_card must be provided")
|
465
|
-
|
466
|
-
return self
|
467
|
-
|
468
|
-
|
469
|
-
class BookResponse(ApiModel):
|
470
|
-
"""Response from book reservation."""
|
471
|
-
|
472
|
-
reservation_id: str
|
473
|
-
|
474
|
-
|
475
|
-
class ListReservationsRequest(ApiModel):
|
476
|
-
"""Request for listing reservations."""
|
477
|
-
|
478
|
-
is_archive: bool = False
|
479
|
-
|
480
|
-
|
481
|
-
class ListReservationsResponse(ApiModel):
|
482
|
-
"""Response from list reservations."""
|
483
|
-
|
484
|
-
success: bool
|
485
|
-
reservations: list[Reservation] = Field(default_factory=list)
|
486
|
-
error: Optional[str] = None
|
487
|
-
|
488
|
-
|
489
|
-
class GetReservationRequest(ApiModel):
|
490
|
-
"""Request for getting reservation details."""
|
491
|
-
|
492
|
-
confirmation: str
|
493
|
-
|
494
|
-
|
495
|
-
class GetReservationResponse(ApiModel):
|
496
|
-
"""Response from get reservation."""
|
497
|
-
|
498
|
-
reservation: EditableReservationRequest
|
499
|
-
is_editable: bool
|
500
|
-
status: Optional[ReservationStatus] = None
|
501
|
-
is_cancellation_pending: bool
|
502
|
-
car_description: Optional[str] = None
|
503
|
-
cancellation_policy: Optional[str] = None
|
504
|
-
pickup_type: LocationType
|
505
|
-
pickup_description: str
|
506
|
-
dropoff_type: LocationType
|
507
|
-
dropoff_description: str
|
508
|
-
additional_services: Optional[str] = None
|
509
|
-
payment_method: Optional[str] = None
|
510
|
-
breakdown: list[BreakdownItem] = Field(default_factory=list)
|
511
|
-
passenger_name: Optional[str] = None
|
512
|
-
evoucher_url: Optional[str] = None
|
513
|
-
receipt_url: Optional[str] = None
|
514
|
-
pending_changes: list[list[str]] = Field(default_factory=list)
|
515
|
-
|
516
|
-
|
517
|
-
class EditReservationResponse(ApiModel):
|
518
|
-
"""Response from edit reservation."""
|
519
|
-
|
520
|
-
success: bool
|
bookalimo/services/pricing.py
CHANGED
@@ -1,14 +1,10 @@
|
|
1
1
|
"""Pricing service for getting quotes and updating trip details."""
|
2
2
|
|
3
|
-
from
|
4
|
-
|
5
|
-
from ..schemas.booking import (
|
3
|
+
from ..schemas import (
|
6
4
|
DetailsRequest,
|
7
5
|
DetailsResponse,
|
8
|
-
Location,
|
9
6
|
PriceRequest,
|
10
7
|
PriceResponse,
|
11
|
-
RateType,
|
12
8
|
)
|
13
9
|
from ..transport.base import AsyncBaseTransport, BaseTransport
|
14
10
|
|
@@ -19,86 +15,28 @@ class AsyncPricingService:
|
|
19
15
|
def __init__(self, transport: AsyncBaseTransport):
|
20
16
|
self._transport = transport
|
21
17
|
|
22
|
-
async def quote(
|
23
|
-
self,
|
24
|
-
rate_type: RateType,
|
25
|
-
date_time: str,
|
26
|
-
pickup: Location,
|
27
|
-
dropoff: Location,
|
28
|
-
passengers: int,
|
29
|
-
luggage: int,
|
30
|
-
**opts: Any,
|
31
|
-
) -> PriceResponse:
|
18
|
+
async def quote(self, request: PriceRequest) -> PriceResponse:
|
32
19
|
"""
|
33
20
|
Get pricing for a trip.
|
34
21
|
|
35
22
|
Args:
|
36
|
-
|
37
|
-
date_time: Date and time in 'MM/dd/yyyy hh:mm tt' format
|
38
|
-
pickup: Pickup location
|
39
|
-
dropoff: Dropoff location
|
40
|
-
passengers: Number of passengers
|
41
|
-
luggage: Number of luggage pieces
|
42
|
-
**opts: Optional fields like hours, stops, account, car_class_code,
|
43
|
-
passenger, rewards, pets, car_seats, boosters, infants,
|
44
|
-
customer_comment
|
23
|
+
request: Complete pricing request with all trip details
|
45
24
|
|
46
25
|
Returns:
|
47
26
|
PriceResponse with pricing information and session token
|
48
27
|
"""
|
49
|
-
# Build request with optional fields
|
50
|
-
request_data: dict[str, Any] = {
|
51
|
-
"rate_type": rate_type,
|
52
|
-
"date_time": date_time,
|
53
|
-
"pickup": pickup,
|
54
|
-
"dropoff": dropoff,
|
55
|
-
"passengers": passengers,
|
56
|
-
"luggage": luggage,
|
57
|
-
}
|
58
|
-
|
59
|
-
# Add optional fields if provided
|
60
|
-
optional_fields = [
|
61
|
-
"hours",
|
62
|
-
"stops",
|
63
|
-
"account",
|
64
|
-
"passenger",
|
65
|
-
"rewards",
|
66
|
-
"car_class_code",
|
67
|
-
"pets",
|
68
|
-
"car_seats",
|
69
|
-
"boosters",
|
70
|
-
"infants",
|
71
|
-
"customer_comment",
|
72
|
-
]
|
73
|
-
|
74
|
-
for field in optional_fields:
|
75
|
-
if field in opts and opts[field] is not None:
|
76
|
-
request_data[field] = opts[field]
|
77
|
-
|
78
|
-
request = PriceRequest(**request_data)
|
79
28
|
return await self._transport.post("/booking/price/", request, PriceResponse)
|
80
29
|
|
81
|
-
async def update_details(self,
|
30
|
+
async def update_details(self, request: DetailsRequest) -> DetailsResponse:
|
82
31
|
"""
|
83
32
|
Update reservation details and get updated pricing.
|
84
33
|
|
85
34
|
Args:
|
86
|
-
|
87
|
-
**details: Fields to update (car_class_code, pickup, dropoff,
|
88
|
-
stops, account, passenger, rewards, pets, car_seats,
|
89
|
-
boosters, infants, customer_comment, ta_fee)
|
35
|
+
request: Complete details request with token and fields to update
|
90
36
|
|
91
37
|
Returns:
|
92
38
|
DetailsResponse with updated pricing
|
93
39
|
"""
|
94
|
-
request_data: dict[str, Any] = {"token": token}
|
95
|
-
|
96
|
-
# Add provided details
|
97
|
-
for key, value in details.items():
|
98
|
-
if value is not None:
|
99
|
-
request_data[key] = value
|
100
|
-
|
101
|
-
request = DetailsRequest(**request_data)
|
102
40
|
return await self._transport.post("/booking/details/", request, DetailsResponse)
|
103
41
|
|
104
42
|
|
@@ -108,84 +46,26 @@ class PricingService:
|
|
108
46
|
def __init__(self, transport: BaseTransport):
|
109
47
|
self._transport = transport
|
110
48
|
|
111
|
-
def quote(
|
112
|
-
self,
|
113
|
-
rate_type: RateType,
|
114
|
-
date_time: str,
|
115
|
-
pickup: Location,
|
116
|
-
dropoff: Location,
|
117
|
-
passengers: int,
|
118
|
-
luggage: int,
|
119
|
-
**opts: Any,
|
120
|
-
) -> PriceResponse:
|
49
|
+
def quote(self, request: PriceRequest) -> PriceResponse:
|
121
50
|
"""
|
122
51
|
Get pricing for a trip.
|
123
52
|
|
124
53
|
Args:
|
125
|
-
|
126
|
-
date_time: Date and time in 'MM/dd/yyyy hh:mm tt' format
|
127
|
-
pickup: Pickup location
|
128
|
-
dropoff: Location
|
129
|
-
passengers: Number of passengers
|
130
|
-
luggage: Number of luggage pieces
|
131
|
-
**opts: Optional fields like hours, stops, account, car_class_code,
|
132
|
-
passenger, rewards, pets, car_seats, boosters, infants,
|
133
|
-
customer_comment
|
54
|
+
request: Complete pricing request with all trip details
|
134
55
|
|
135
56
|
Returns:
|
136
57
|
PriceResponse with pricing information and session token
|
137
58
|
"""
|
138
|
-
# Build request with optional fields
|
139
|
-
request_data: dict[str, Any] = {
|
140
|
-
"rate_type": rate_type,
|
141
|
-
"date_time": date_time,
|
142
|
-
"pickup": pickup,
|
143
|
-
"dropoff": dropoff,
|
144
|
-
"passengers": passengers,
|
145
|
-
"luggage": luggage,
|
146
|
-
}
|
147
|
-
|
148
|
-
# Add optional fields if provided
|
149
|
-
optional_fields = [
|
150
|
-
"hours",
|
151
|
-
"stops",
|
152
|
-
"account",
|
153
|
-
"passenger",
|
154
|
-
"rewards",
|
155
|
-
"car_class_code",
|
156
|
-
"pets",
|
157
|
-
"car_seats",
|
158
|
-
"boosters",
|
159
|
-
"infants",
|
160
|
-
"customer_comment",
|
161
|
-
]
|
162
|
-
|
163
|
-
for field in optional_fields:
|
164
|
-
if field in opts and opts[field] is not None:
|
165
|
-
request_data[field] = opts[field]
|
166
|
-
|
167
|
-
request = PriceRequest(**request_data)
|
168
59
|
return self._transport.post("/booking/price/", request, PriceResponse)
|
169
60
|
|
170
|
-
def update_details(self,
|
61
|
+
def update_details(self, request: DetailsRequest) -> DetailsResponse:
|
171
62
|
"""
|
172
63
|
Update reservation details and get updated pricing.
|
173
64
|
|
174
65
|
Args:
|
175
|
-
|
176
|
-
**details: Fields to update (car_class_code, pickup, dropoff,
|
177
|
-
stops, account, passenger, rewards, pets, car_seats,
|
178
|
-
boosters, infants, customer_comment, ta_fee)
|
66
|
+
request: Complete details request with token and fields to update
|
179
67
|
|
180
68
|
Returns:
|
181
69
|
DetailsResponse with updated pricing
|
182
70
|
"""
|
183
|
-
request_data: dict[str, Any] = {"token": token}
|
184
|
-
|
185
|
-
# Add provided details
|
186
|
-
for key, value in details.items():
|
187
|
-
if value is not None:
|
188
|
-
request_data[key] = value
|
189
|
-
|
190
|
-
request = DetailsRequest(**request_data)
|
191
71
|
return self._transport.post("/booking/details/", request, DetailsResponse)
|