Spaces:
Paused
Paused
backend: airline/chain-direct offers in flight+hotel HTTP scoring
Browse files- app/main.py +25 -8
app/main.py
CHANGED
|
@@ -188,6 +188,13 @@ class FlightSellersRequest(BaseModel):
|
|
| 188 |
held_cards: Optional[List[str]] = None
|
| 189 |
persona: Optional[List[str]] = None
|
| 190 |
city: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
include_comparison: Optional[bool] = False # app scores on-device by default
|
| 192 |
|
| 193 |
|
|
@@ -224,6 +231,9 @@ class HotelSellersRequest(BaseModel):
|
|
| 224 |
adults: Optional[int] = 2
|
| 225 |
held_cards: Optional[List[str]] = None
|
| 226 |
persona: Optional[List[str]] = None
|
|
|
|
|
|
|
|
|
|
| 227 |
include_comparison: Optional[bool] = False
|
| 228 |
|
| 229 |
|
|
@@ -498,7 +508,7 @@ def flights_search(req: FlightSearchRequest, request: Request = None):
|
|
| 498 |
_limit(request, "flt", rate=30) # 30/min per IP per worker
|
| 499 |
import flights_provider
|
| 500 |
import flight_sellers
|
| 501 |
-
from channels import compare_channels
|
| 502 |
from availability import normalize_city
|
| 503 |
|
| 504 |
held = req.held_cards or demo_data.DEMO_WALLET
|
|
@@ -564,7 +574,9 @@ def flights_search(req: FlightSearchRequest, request: Request = None):
|
|
| 564 |
for f in flights:
|
| 565 |
cmp = compare_channels(
|
| 566 |
held, "travel_flights", float(f["fare_inr"]),
|
| 567 |
-
|
|
|
|
|
|
|
| 568 |
persona=persona, city=city, international=intl,
|
| 569 |
# The party this search is for. Without it the server always scored
|
| 570 |
# 1 passenger one-way while the app scored the real party, so the two
|
|
@@ -616,7 +628,7 @@ def flights_sellers(req: FlightSellersRequest, request: Request = None):
|
|
| 616 |
if request is not None:
|
| 617 |
_limit(request, "fls", rate=20) # 20/min per IP per worker
|
| 618 |
import flight_sellers
|
| 619 |
-
from channels import compare_channels
|
| 620 |
from availability import normalize_city
|
| 621 |
import geo
|
| 622 |
|
|
@@ -633,7 +645,9 @@ def flights_sellers(req: FlightSellersRequest, request: Request = None):
|
|
| 633 |
base = min(data["channel_prices"].values()) if data["channel_prices"] else 0.0
|
| 634 |
cmp = compare_channels(
|
| 635 |
held, "travel_flights", float(base),
|
| 636 |
-
|
|
|
|
|
|
|
| 637 |
persona=req.persona or demo_data.DEMO_PERSONA,
|
| 638 |
city=normalize_city(req.city),
|
| 639 |
international=geo.route_is_international(req.src, req.dst),
|
|
@@ -691,7 +705,7 @@ def hotels_search(req: HotelSearchRequest, request: Request = None):
|
|
| 691 |
if request is not None:
|
| 692 |
_limit(request, "htl", rate=30)
|
| 693 |
import hotels_provider
|
| 694 |
-
from channels import compare_channels
|
| 695 |
from availability import normalize_city
|
| 696 |
|
| 697 |
held = req.held_cards or demo_data.DEMO_WALLET
|
|
@@ -717,7 +731,8 @@ def hotels_search(req: HotelSearchRequest, request: Request = None):
|
|
| 717 |
for h in hotels:
|
| 718 |
cmp = compare_channels(
|
| 719 |
held, "travel_hotels", float(h["price_inr"]),
|
| 720 |
-
|
|
|
|
| 721 |
persona=persona, city=city, international=intl,
|
| 722 |
) if want_cmp else None
|
| 723 |
results.append({"hotel": h, "channel_comparison": cmp})
|
|
@@ -749,7 +764,7 @@ def hotels_sellers(req: HotelSellersRequest, request: Request = None):
|
|
| 749 |
if request is not None:
|
| 750 |
_limit(request, "hts", rate=20)
|
| 751 |
import hotel_sellers
|
| 752 |
-
from channels import compare_channels
|
| 753 |
from availability import normalize_city
|
| 754 |
import geo
|
| 755 |
|
|
@@ -763,7 +778,9 @@ def hotels_sellers(req: HotelSellersRequest, request: Request = None):
|
|
| 763 |
base = min(data["channel_prices"].values())
|
| 764 |
cmp = compare_channels(
|
| 765 |
held, "travel_hotels", float(base),
|
| 766 |
-
|
|
|
|
|
|
|
| 767 |
persona=req.persona or demo_data.DEMO_PERSONA,
|
| 768 |
city=normalize_city(req.city) or req.city,
|
| 769 |
international=geo.city_is_international(req.city),
|
|
|
|
| 188 |
held_cards: Optional[List[str]] = None
|
| 189 |
persona: Optional[List[str]] = None
|
| 190 |
city: Optional[str] = None
|
| 191 |
+
# The picked flight's airline, so the DIRECT channel scores that airline's
|
| 192 |
+
# own offers (Air India's flat Rs3,000 Axis discount, etc.) - the on-device
|
| 193 |
+
# app passes this via airlineBrandKey; without it the endpoint's optional
|
| 194 |
+
# comparison scored the airline website with no airline and missed those
|
| 195 |
+
# deals (2026-08-11 international app-vs-endpoint test). Optional: an older
|
| 196 |
+
# client that omits it gets exactly the prior behaviour.
|
| 197 |
+
airline: Optional[str] = None
|
| 198 |
include_comparison: Optional[bool] = False # app scores on-device by default
|
| 199 |
|
| 200 |
|
|
|
|
| 231 |
adults: Optional[int] = 2
|
| 232 |
held_cards: Optional[List[str]] = None
|
| 233 |
persona: Optional[List[str]] = None
|
| 234 |
+
# The picked property's name, so the DIRECT channel scores that chain's own
|
| 235 |
+
# offers (Marriott/IHG etc.) - mirror of the app's hotelBrandKey. Optional.
|
| 236 |
+
property_name: Optional[str] = None
|
| 237 |
include_comparison: Optional[bool] = False
|
| 238 |
|
| 239 |
|
|
|
|
| 508 |
_limit(request, "flt", rate=30) # 30/min per IP per worker
|
| 509 |
import flights_provider
|
| 510 |
import flight_sellers
|
| 511 |
+
from channels import compare_channels, airline_brand_key
|
| 512 |
from availability import normalize_city
|
| 513 |
|
| 514 |
held = req.held_cards or demo_data.DEMO_WALLET
|
|
|
|
| 574 |
for f in flights:
|
| 575 |
cmp = compare_channels(
|
| 576 |
held, "travel_flights", float(f["fare_inr"]),
|
| 577 |
+
# This flight's own airline, so "Airline website" scores that
|
| 578 |
+
# carrier's offers. Mirror of the app's per-row airlineBrandKey.
|
| 579 |
+
brand_key=airline_brand_key(f.get("airline")), merchant=None, rail="card",
|
| 580 |
persona=persona, city=city, international=intl,
|
| 581 |
# The party this search is for. Without it the server always scored
|
| 582 |
# 1 passenger one-way while the app scored the real party, so the two
|
|
|
|
| 628 |
if request is not None:
|
| 629 |
_limit(request, "fls", rate=20) # 20/min per IP per worker
|
| 630 |
import flight_sellers
|
| 631 |
+
from channels import compare_channels, airline_brand_key
|
| 632 |
from availability import normalize_city
|
| 633 |
import geo
|
| 634 |
|
|
|
|
| 645 |
base = min(data["channel_prices"].values()) if data["channel_prices"] else 0.0
|
| 646 |
cmp = compare_channels(
|
| 647 |
held, "travel_flights", float(base),
|
| 648 |
+
# The picked flight's airline (sent by the client), so the direct
|
| 649 |
+
# channel scores that carrier's offers - matches the app.
|
| 650 |
+
brand_key=airline_brand_key(req.airline), merchant=None, rail="card",
|
| 651 |
persona=req.persona or demo_data.DEMO_PERSONA,
|
| 652 |
city=normalize_city(req.city),
|
| 653 |
international=geo.route_is_international(req.src, req.dst),
|
|
|
|
| 705 |
if request is not None:
|
| 706 |
_limit(request, "htl", rate=30)
|
| 707 |
import hotels_provider
|
| 708 |
+
from channels import compare_channels, hotel_brand_key
|
| 709 |
from availability import normalize_city
|
| 710 |
|
| 711 |
held = req.held_cards or demo_data.DEMO_WALLET
|
|
|
|
| 731 |
for h in hotels:
|
| 732 |
cmp = compare_channels(
|
| 733 |
held, "travel_hotels", float(h["price_inr"]),
|
| 734 |
+
# This property's chain, so "Hotel directly" scores its own offers.
|
| 735 |
+
brand_key=hotel_brand_key(h.get("name")), merchant=None, rail="card",
|
| 736 |
persona=persona, city=city, international=intl,
|
| 737 |
) if want_cmp else None
|
| 738 |
results.append({"hotel": h, "channel_comparison": cmp})
|
|
|
|
| 764 |
if request is not None:
|
| 765 |
_limit(request, "hts", rate=20)
|
| 766 |
import hotel_sellers
|
| 767 |
+
from channels import compare_channels, hotel_brand_key
|
| 768 |
from availability import normalize_city
|
| 769 |
import geo
|
| 770 |
|
|
|
|
| 778 |
base = min(data["channel_prices"].values())
|
| 779 |
cmp = compare_channels(
|
| 780 |
held, "travel_hotels", float(base),
|
| 781 |
+
# The picked property's chain (sent by the client), so the direct
|
| 782 |
+
# channel scores its own offers - matches the app.
|
| 783 |
+
brand_key=hotel_brand_key(req.property_name), merchant=None, rail="card",
|
| 784 |
persona=req.persona or demo_data.DEMO_PERSONA,
|
| 785 |
city=normalize_city(req.city) or req.city,
|
| 786 |
international=geo.city_is_international(req.city),
|