Surface OTA seller diversity in booking results (fixture, tests, example, docs)#25
Merged
Conversation
Issue #24 asked whether get_booking_results() can surface the full OTA seller list (Expedia, FlightHub, ...) or only airline-direct fares. The parser already surfaces every seller Google returns, but the entire booking corpus was premium-cabin / major-carrier itineraries, which are all airline-direct — so nothing exercised the OTA path (including the option[21] null-brand fallback added in b9dfd7d). Captures a real OTA-bearing response (SFO->MNL economy on Philippine Airlines: PR airline-direct + 14 OTAs, all null-brand) as a contract-corpus fixture, and adds tests/test_booking.py asserting the OTA list is surfaced, airline-direct splits cleanly from resellers, null-brand options are kept not dropped, and OTA options carry booking URLs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Answers issue #24 for users: get_booking_results() returns one BookingOption per booking channel (airline-direct + OTAs), and whether OTAs appear is Google's call (premium/major-carrier US = airline-direct only; international economy on foreign carriers = OTA wall). - examples/booking_options.py: runnable script that splits airline-direct from OTAs, prints price/seller/brand per channel, and surfaces the cheapest booking_url. Defaults to an OTA-rich route so diversity shows out of the box. - README + examples/README: document the channel model and when OTAs appear. - proto notes: record observed OTA seller codes and the null-brand behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Transient agent worktree checkouts under .claude/worktrees/ duplicate the whole repo tree (swoop/ + tests/), so pyright was scanning a second copy and reporting hundreds of errors from the excluded tests/ directory. Exclude .claude/worktrees from pyright and gitignore it so the scaffolding never pollutes the typecheck gate or gets staged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The full 'when do OTAs appear' explanation already lives in the example docstring and booking proto notes; the README only needs to orient and link out. Drops the second code snippet and the premium-vs-international paragraph. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Closes #24. A user asked whether
get_booking_results()can surface the full OTA seller list (Expedia, FlightHub, …) that the Google Flights web UI shows, or whether swoop is limited to airline-direct fares.It already works correctly — the parser surfaces every seller Google returns via
opt[1]. The catch: Google only includes OTAs for itineraries it offers them on (international economy on foreign carriers). Every one of the 10 booking corpus fixtures was a premium-cabin / major-carrier itinerary, which are all airline-direct, so nothing in the repo demonstrated the OTA path — and theoption[21]null-brand fallback (added inb9dfd7d) had no fixture coverage.Verified live: SFO→MNL economy on Philippine Airlines returns 1 airline-direct (
PR) + 14 OTAs (EXPEDIA,FLIGHTHUB,CHEAPOAIR,BYOJET, …), each with its ownbooking_url.What
tests/test_booking.py— corpus-backed regression tests: the OTA list is surfaced,is_airline_directsplits the operating carrier from resellers, null-brand options are kept (not dropped), and OTA options carrygoogle.com/travel/clkredirects.tests/fixtures/corpus/booking_intl_economy_ota_response.txt+ manifest casebooking_intl_economy_ota_v1— a real captured OTA-bearing response, replayed offline. Locks inseller_codes/is_airline_directso a futureopt[1]reshape fails loudly.examples/booking_options.py— runnable: splits airline-direct from OTAs, prints price/seller/brand per channel, surfaces the cheapestbooking_url. Defaults to an OTA-rich route so seller diversity shows out of the box.BookingOptionis a booking channel and that OTA visibility is Google's call, not a swoop limit.pyproject.toml/.gitignore— exclude transient.claude/worktreescheckouts from pyright (they duplicated the repo tree and produced phantom errors) and gitignore the scaffolding.Test plan
make check→ 1420 passed, 54 skipped, typecheck 0 errors.python examples/booking_options.py SFO MNL <date> --cabin economy→ printed 15 distinct sellers (PR + 14 OTAs) with booking URLs.🤖 Generated with Claude Code