bookalimo 0.1.4__py3-none-any.whl → 1.0.0__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/__init__.py +17 -24
- bookalimo/_version.py +9 -0
- bookalimo/client.py +310 -0
- bookalimo/config.py +16 -0
- bookalimo/exceptions.py +115 -5
- bookalimo/integrations/__init__.py +1 -0
- bookalimo/integrations/google_places/__init__.py +31 -0
- bookalimo/integrations/google_places/client_async.py +258 -0
- bookalimo/integrations/google_places/client_sync.py +257 -0
- bookalimo/integrations/google_places/common.py +245 -0
- bookalimo/integrations/google_places/proto_adapter.py +224 -0
- bookalimo/{_logging.py → logging.py} +59 -62
- bookalimo/schemas/__init__.py +97 -0
- bookalimo/schemas/base.py +56 -0
- bookalimo/{models.py → schemas/booking.py} +88 -100
- bookalimo/schemas/places/__init__.py +37 -0
- bookalimo/schemas/places/common.py +198 -0
- bookalimo/schemas/places/google.py +596 -0
- bookalimo/schemas/places/place.py +337 -0
- bookalimo/services/__init__.py +11 -0
- bookalimo/services/pricing.py +191 -0
- bookalimo/services/reservations.py +227 -0
- bookalimo/transport/__init__.py +7 -0
- bookalimo/transport/auth.py +41 -0
- bookalimo/transport/base.py +44 -0
- bookalimo/transport/httpx_async.py +230 -0
- bookalimo/transport/httpx_sync.py +230 -0
- bookalimo/transport/retry.py +102 -0
- bookalimo/transport/utils.py +59 -0
- bookalimo-1.0.0.dist-info/METADATA +307 -0
- bookalimo-1.0.0.dist-info/RECORD +35 -0
- bookalimo/_client.py +0 -420
- bookalimo/wrapper.py +0 -444
- bookalimo-0.1.4.dist-info/METADATA +0 -392
- bookalimo-0.1.4.dist-info/RECORD +0 -12
- {bookalimo-0.1.4.dist-info → bookalimo-1.0.0.dist-info}/WHEEL +0 -0
- {bookalimo-0.1.4.dist-info → bookalimo-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {bookalimo-0.1.4.dist-info → bookalimo-1.0.0.dist-info}/top_level.txt +0 -0
bookalimo/wrapper.py
DELETED
@@ -1,444 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
High-level API wrapper for Book-A-Limo operations.
|
3
|
-
Provides clean, LLM-friendly functions that abstract API complexities.
|
4
|
-
"""
|
5
|
-
|
6
|
-
import logging
|
7
|
-
from types import TracebackType
|
8
|
-
from typing import Any, Optional
|
9
|
-
|
10
|
-
from httpx import AsyncClient
|
11
|
-
|
12
|
-
from ._client import BookALimoClient
|
13
|
-
from ._logging import get_logger, log_call
|
14
|
-
from .exceptions import BookALimoError
|
15
|
-
from .models import (
|
16
|
-
Address,
|
17
|
-
Airport,
|
18
|
-
BookRequest,
|
19
|
-
BookResponse,
|
20
|
-
CardHolderType,
|
21
|
-
City,
|
22
|
-
Credentials,
|
23
|
-
CreditCard,
|
24
|
-
DetailsRequest,
|
25
|
-
DetailsResponse,
|
26
|
-
EditableReservationRequest,
|
27
|
-
EditReservationResponse,
|
28
|
-
GetReservationResponse,
|
29
|
-
ListReservationsResponse,
|
30
|
-
Location,
|
31
|
-
LocationType,
|
32
|
-
Passenger,
|
33
|
-
PriceRequest,
|
34
|
-
PriceResponse,
|
35
|
-
RateType,
|
36
|
-
Stop,
|
37
|
-
)
|
38
|
-
|
39
|
-
logger = get_logger("wrapper")
|
40
|
-
|
41
|
-
|
42
|
-
class BookALimo:
|
43
|
-
"""
|
44
|
-
High-level wrapper for Book-A-Limo API operations.
|
45
|
-
Provides small, LLM-friendly functions that map 1:1 to API endpoints.
|
46
|
-
"""
|
47
|
-
|
48
|
-
def __init__(
|
49
|
-
self,
|
50
|
-
credentials: Credentials,
|
51
|
-
http_client: Optional[AsyncClient] = None,
|
52
|
-
base_url: str = "https://www.bookalimo.com/web/api",
|
53
|
-
http_timeout: float = 5.0,
|
54
|
-
**kwargs: Any,
|
55
|
-
):
|
56
|
-
"""
|
57
|
-
Initializes the BookALimo API wrapper.
|
58
|
-
|
59
|
-
Args:
|
60
|
-
credentials: User ID and password hash for authentication.
|
61
|
-
http_client: Optional custom httpx.AsyncClient instance.
|
62
|
-
**kwargs: Additional options passed to the BookALimoClient.
|
63
|
-
"""
|
64
|
-
self._owns_http_client = http_client is None
|
65
|
-
self.http_client = http_client or AsyncClient()
|
66
|
-
self.client = BookALimoClient(
|
67
|
-
credentials=credentials,
|
68
|
-
client=self.http_client,
|
69
|
-
base_url=base_url,
|
70
|
-
http_timeout=http_timeout,
|
71
|
-
**kwargs,
|
72
|
-
)
|
73
|
-
if logger.isEnabledFor(logging.DEBUG): # NEW: tiny, safe init log
|
74
|
-
logger.debug(
|
75
|
-
"BookALimo initialized (base_url=%s, timeout=%s, owns_http_client=%s)",
|
76
|
-
base_url,
|
77
|
-
http_timeout,
|
78
|
-
self._owns_http_client,
|
79
|
-
)
|
80
|
-
|
81
|
-
async def aclose(self) -> None:
|
82
|
-
"""Close the HTTP client if we own it."""
|
83
|
-
if self._owns_http_client and not self.http_client.is_closed:
|
84
|
-
await self.http_client.aclose()
|
85
|
-
if logger.isEnabledFor(logging.DEBUG):
|
86
|
-
logger.debug("HTTP client closed")
|
87
|
-
|
88
|
-
async def __aenter__(self) -> "BookALimo":
|
89
|
-
"""Async context manager entry."""
|
90
|
-
return self
|
91
|
-
|
92
|
-
async def __aexit__(
|
93
|
-
self,
|
94
|
-
exc_type: Optional[type[BaseException]],
|
95
|
-
exc_val: Optional[BaseException],
|
96
|
-
exc_tb: Optional[TracebackType],
|
97
|
-
) -> None:
|
98
|
-
"""Async context manager exit."""
|
99
|
-
await self.aclose()
|
100
|
-
|
101
|
-
@log_call(include_params=["is_archive"], operation="list_reservations")
|
102
|
-
async def list_reservations(
|
103
|
-
self, is_archive: bool = False
|
104
|
-
) -> ListReservationsResponse:
|
105
|
-
"""
|
106
|
-
List reservations for the user.
|
107
|
-
|
108
|
-
Args:
|
109
|
-
is_archive: If True, fetch archived reservations
|
110
|
-
|
111
|
-
Returns:
|
112
|
-
Dict with 'success', 'reservations' list, optional 'error'
|
113
|
-
"""
|
114
|
-
try:
|
115
|
-
result = await self.client.list_reservations(is_archive)
|
116
|
-
|
117
|
-
return result
|
118
|
-
except Exception as e:
|
119
|
-
raise BookALimoError(f"Failed to list reservations: {str(e)}") from e
|
120
|
-
|
121
|
-
@log_call(include_params=["confirmation"], operation="get_reservation")
|
122
|
-
async def get_reservation(self, confirmation: str) -> GetReservationResponse:
|
123
|
-
"""
|
124
|
-
Get detailed reservation information.
|
125
|
-
|
126
|
-
Args:
|
127
|
-
confirmation: Confirmation number
|
128
|
-
|
129
|
-
Returns:
|
130
|
-
Dict with reservation details, status, policies, breakdown
|
131
|
-
"""
|
132
|
-
try:
|
133
|
-
result = await self.client.get_reservation(confirmation)
|
134
|
-
|
135
|
-
return result
|
136
|
-
except Exception as e:
|
137
|
-
raise BookALimoError(f"Failed to get reservation: {str(e)}") from e
|
138
|
-
|
139
|
-
@log_call(
|
140
|
-
include_params=[
|
141
|
-
"rate_type",
|
142
|
-
"date_time",
|
143
|
-
"passengers",
|
144
|
-
"luggage",
|
145
|
-
],
|
146
|
-
operation="get_prices",
|
147
|
-
)
|
148
|
-
async def get_prices(
|
149
|
-
self,
|
150
|
-
rate_type: RateType,
|
151
|
-
date_time: str,
|
152
|
-
pickup: Location,
|
153
|
-
dropoff: Location,
|
154
|
-
passengers: int,
|
155
|
-
luggage: int,
|
156
|
-
**kwargs: Any,
|
157
|
-
) -> PriceResponse:
|
158
|
-
"""
|
159
|
-
Get pricing for a trip.
|
160
|
-
|
161
|
-
Args:
|
162
|
-
rate_type: 0=P2P, 1=Hourly (or string names)
|
163
|
-
date_time: 'MM/dd/yyyy hh:mm tt' format
|
164
|
-
pickup_location: Location
|
165
|
-
dropoff_location: Location
|
166
|
-
passengers: Number of passengers
|
167
|
-
luggage: Number of luggage pieces
|
168
|
-
**kwargs: Optional fields like stops, account, car_class_code, etc.
|
169
|
-
|
170
|
-
Returns:
|
171
|
-
Dict with 'token' and 'prices' list
|
172
|
-
"""
|
173
|
-
try:
|
174
|
-
# Build request with optional fields
|
175
|
-
request_data: dict[str, Any] = {
|
176
|
-
"rate_type": rate_type,
|
177
|
-
"date_time": date_time,
|
178
|
-
"pickup": pickup,
|
179
|
-
"dropoff": dropoff,
|
180
|
-
"passengers": passengers,
|
181
|
-
"luggage": luggage,
|
182
|
-
}
|
183
|
-
|
184
|
-
# Add optional fields if provided
|
185
|
-
optional_fields = [
|
186
|
-
"hours",
|
187
|
-
"stops",
|
188
|
-
"account",
|
189
|
-
"passenger",
|
190
|
-
"rewards",
|
191
|
-
"car_class_code",
|
192
|
-
"pets",
|
193
|
-
"car_seats",
|
194
|
-
"boosters",
|
195
|
-
"infants",
|
196
|
-
"customer_comment",
|
197
|
-
]
|
198
|
-
|
199
|
-
for field in optional_fields:
|
200
|
-
if field in kwargs and kwargs[field] is not None:
|
201
|
-
request_data[field] = kwargs[field]
|
202
|
-
|
203
|
-
request_model = PriceRequest(**request_data)
|
204
|
-
|
205
|
-
result = await self.client.get_prices(request_model)
|
206
|
-
|
207
|
-
return result
|
208
|
-
except Exception as e:
|
209
|
-
raise BookALimoError(f"Failed to get prices: {str(e)}") from e
|
210
|
-
|
211
|
-
@log_call(
|
212
|
-
include_params=["token", "details"],
|
213
|
-
transforms={
|
214
|
-
"details": lambda d: sorted(
|
215
|
-
[k for k, v in (d or {}).items() if v is not None]
|
216
|
-
),
|
217
|
-
},
|
218
|
-
operation="set_details",
|
219
|
-
)
|
220
|
-
async def set_details(self, token: str, **details: Any) -> DetailsResponse:
|
221
|
-
"""
|
222
|
-
Set reservation details and get updated pricing.
|
223
|
-
|
224
|
-
Args:
|
225
|
-
token: Session token from get_prices
|
226
|
-
**details: Fields to update (car_class_code, pickup, dropoff,
|
227
|
-
stops, account, passenger, rewards, pets, car_seats,
|
228
|
-
boosters, infants, customer_comment, ta_fee)
|
229
|
-
|
230
|
-
Returns:
|
231
|
-
Dict with 'price' and 'breakdown' list
|
232
|
-
"""
|
233
|
-
try:
|
234
|
-
request_data: dict[str, Any] = {"token": token}
|
235
|
-
|
236
|
-
# Add provided details
|
237
|
-
for key, value in details.items():
|
238
|
-
if value is not None:
|
239
|
-
request_data[key] = value
|
240
|
-
|
241
|
-
request_model = DetailsRequest(**request_data)
|
242
|
-
|
243
|
-
result = await self.client.set_details(request_model)
|
244
|
-
|
245
|
-
return result
|
246
|
-
except Exception as e:
|
247
|
-
raise BookALimoError(f"Failed to set details: {str(e)}") from e
|
248
|
-
|
249
|
-
@log_call(
|
250
|
-
include_params=["token", "method", "promo", "credit_card"],
|
251
|
-
operation="book",
|
252
|
-
)
|
253
|
-
async def book(
|
254
|
-
self,
|
255
|
-
token: str,
|
256
|
-
method: Optional[str] = None,
|
257
|
-
credit_card: Optional[CreditCard] = None,
|
258
|
-
promo: Optional[str] = None,
|
259
|
-
) -> BookResponse:
|
260
|
-
"""
|
261
|
-
Book a reservation.
|
262
|
-
|
263
|
-
Args:
|
264
|
-
token: Session token from get_prices/set_details
|
265
|
-
method: 'charge' for charge accounts, None for credit card
|
266
|
-
credit_card: Credit card dict (required if method is not 'charge')
|
267
|
-
promo: Optional promo code
|
268
|
-
|
269
|
-
Returns:
|
270
|
-
Dict with 'reservation_id'
|
271
|
-
"""
|
272
|
-
try:
|
273
|
-
request_data: dict[str, Any] = {"token": token}
|
274
|
-
|
275
|
-
if promo:
|
276
|
-
request_data["promo"] = promo
|
277
|
-
|
278
|
-
if method == "charge":
|
279
|
-
request_data["method"] = "charge"
|
280
|
-
elif credit_card:
|
281
|
-
request_data["credit_card"] = credit_card
|
282
|
-
else:
|
283
|
-
raise BookALimoError(
|
284
|
-
"Either method='charge' or credit_card must be provided"
|
285
|
-
)
|
286
|
-
|
287
|
-
request_model = BookRequest(**request_data)
|
288
|
-
|
289
|
-
result = await self.client.book_reservation(request_model)
|
290
|
-
|
291
|
-
return result
|
292
|
-
except Exception as e:
|
293
|
-
raise BookALimoError(f"Failed to book reservation: {str(e)}") from e
|
294
|
-
|
295
|
-
@log_call(
|
296
|
-
include_params=["confirmation", "is_cancel_request", "changes"],
|
297
|
-
transforms={
|
298
|
-
"changes": lambda d: sorted(
|
299
|
-
[k for k, v in (d or {}).items() if v is not None]
|
300
|
-
),
|
301
|
-
},
|
302
|
-
operation="edit_reservation",
|
303
|
-
)
|
304
|
-
async def edit_reservation(
|
305
|
-
self, confirmation: str, is_cancel_request: bool = False, **changes: Any
|
306
|
-
) -> EditReservationResponse:
|
307
|
-
"""
|
308
|
-
Edit or cancel a reservation.
|
309
|
-
|
310
|
-
Args:
|
311
|
-
confirmation: Confirmation number
|
312
|
-
is_cancel_request: True to cancel the reservation
|
313
|
-
**changes: Fields to change (rate_type, pickup_date, pickup_time,
|
314
|
-
stops, passengers, luggage, pets, car_seats, boosters,
|
315
|
-
infants, other)
|
316
|
-
|
317
|
-
Returns:
|
318
|
-
EditReservationResponse
|
319
|
-
"""
|
320
|
-
try:
|
321
|
-
request_data: dict[str, Any] = {
|
322
|
-
"confirmation": confirmation,
|
323
|
-
"is_cancel_request": is_cancel_request,
|
324
|
-
}
|
325
|
-
|
326
|
-
# Add changes if not canceling
|
327
|
-
if not is_cancel_request:
|
328
|
-
for key, value in changes.items():
|
329
|
-
if value is not None:
|
330
|
-
request_data[key] = value
|
331
|
-
|
332
|
-
request_model = EditableReservationRequest(**request_data)
|
333
|
-
|
334
|
-
return await self.client.edit_reservation(request_model)
|
335
|
-
except Exception as e:
|
336
|
-
raise BookALimoError(f"Failed to edit reservation: {str(e)}") from e
|
337
|
-
|
338
|
-
|
339
|
-
# Convenience functions for creating common data structures
|
340
|
-
|
341
|
-
|
342
|
-
def create_credentials(
|
343
|
-
user_id: str, password: str, is_customer: bool = False
|
344
|
-
) -> Credentials:
|
345
|
-
"""Create credentials dict with proper password hash."""
|
346
|
-
return Credentials(
|
347
|
-
id=user_id,
|
348
|
-
is_customer=is_customer,
|
349
|
-
password_hash=Credentials.create_hash(password, user_id),
|
350
|
-
)
|
351
|
-
|
352
|
-
|
353
|
-
def create_address_location(
|
354
|
-
address: str,
|
355
|
-
google_geocode: Optional[dict[str, Any]] = None,
|
356
|
-
district: Optional[str] = None,
|
357
|
-
building: Optional[str] = None,
|
358
|
-
suite: Optional[str] = None,
|
359
|
-
zip_code: Optional[str] = None,
|
360
|
-
) -> Location:
|
361
|
-
"""Create address-based location dict."""
|
362
|
-
return Location(
|
363
|
-
type=LocationType.ADDRESS,
|
364
|
-
address=Address(
|
365
|
-
city=City(
|
366
|
-
city_name=address,
|
367
|
-
country_code="US",
|
368
|
-
state_code="NY",
|
369
|
-
state_name="New York",
|
370
|
-
),
|
371
|
-
district=district,
|
372
|
-
suite=suite,
|
373
|
-
google_geocode=google_geocode,
|
374
|
-
place_name=address if not google_geocode else None,
|
375
|
-
street_name=address if not google_geocode else None,
|
376
|
-
neighbourhood=district,
|
377
|
-
building=building,
|
378
|
-
zip=zip_code,
|
379
|
-
),
|
380
|
-
)
|
381
|
-
|
382
|
-
|
383
|
-
def create_airport_location(
|
384
|
-
iata_code: str,
|
385
|
-
city_name: str,
|
386
|
-
airline_code: Optional[str] = None,
|
387
|
-
flight_number: Optional[str] = None,
|
388
|
-
terminal: Optional[str] = None,
|
389
|
-
meet_greet: Optional[int] = None,
|
390
|
-
airline_icao_code: Optional[str] = None,
|
391
|
-
) -> Location:
|
392
|
-
"""Create airport-based location dict."""
|
393
|
-
return Location(
|
394
|
-
type=LocationType.AIRPORT,
|
395
|
-
airport=Airport(
|
396
|
-
iata_code=iata_code,
|
397
|
-
country_code="US",
|
398
|
-
state_code="NY",
|
399
|
-
airline_iata_code=airline_code,
|
400
|
-
airline_icao_code=airline_icao_code,
|
401
|
-
flight_number=flight_number,
|
402
|
-
terminal=terminal,
|
403
|
-
arriving_from_city=City(
|
404
|
-
city_name=city_name,
|
405
|
-
country_code="US",
|
406
|
-
state_code="NY",
|
407
|
-
state_name="New York",
|
408
|
-
),
|
409
|
-
meet_greet=meet_greet,
|
410
|
-
),
|
411
|
-
)
|
412
|
-
|
413
|
-
|
414
|
-
def create_stop(description: str, is_en_route: bool = True) -> Stop:
|
415
|
-
"""Create stop dict."""
|
416
|
-
return Stop(description=description, is_en_route=is_en_route)
|
417
|
-
|
418
|
-
|
419
|
-
def create_passenger(
|
420
|
-
first_name: str, last_name: str, phone: str, email: Optional[str] = None
|
421
|
-
) -> Passenger:
|
422
|
-
"""Create passenger dict."""
|
423
|
-
return Passenger(
|
424
|
-
first_name=first_name, last_name=last_name, phone=phone, email=email
|
425
|
-
)
|
426
|
-
|
427
|
-
|
428
|
-
def create_credit_card(
|
429
|
-
number: str,
|
430
|
-
card_holder: str,
|
431
|
-
holder_type: CardHolderType,
|
432
|
-
expiration: str,
|
433
|
-
cvv: str,
|
434
|
-
zip_code: Optional[str] = None,
|
435
|
-
) -> CreditCard:
|
436
|
-
"""Create credit card dict."""
|
437
|
-
return CreditCard(
|
438
|
-
number=number,
|
439
|
-
card_holder=card_holder,
|
440
|
-
holder_type=holder_type,
|
441
|
-
expiration=expiration,
|
442
|
-
cvv=cvv,
|
443
|
-
zip=zip_code,
|
444
|
-
)
|