Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OTT-9 : Support for Deal Prioritisation for CTV / Ad Pod #86

Merged
merged 27 commits into from
Nov 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
dee2ca5
UOE-5440: Changes for capturing Pod algorithm execution time using pb…
ShriprasadM Aug 14, 2020
db9a643
UOE-5440: Fixed the Unit test issues (#72)
ShriprasadM Aug 14, 2020
501927a
UOE-5511 Support for skadnetwork in pubmatic (#73)
PubMatic-OpenWrap Aug 20, 2020
a059556
Merge branch 'master' into ci
pm-shriprasad-marathe Sep 1, 2020
ebe98c7
Resolved merge issues
pm-shriprasad-marathe Sep 1, 2020
dc4d192
BugID:OTT-17 First Commit
pm-viral-vala Sep 9, 2020
58b356d
OTT-18: moved VideoAuction to selector pattern. This required for mo…
ShriprasadM Sep 15, 2020
47de6a5
Merge branch 'OTT-9' into ci
pm-viral-vala Sep 17, 2020
e5648b4
OTT-24: Basic support for sorting the deal bids in forming the final …
pm-shriprasad-marathe Sep 21, 2020
0f8f81e
OTT-24: Added changes around https://github.com/prebid/prebid-server/…
pm-shriprasad-marathe Sep 24, 2020
766a92a
OTT-27, OTT-32 Supporting Deal Prioritization for CTV
pm-viral-vala Sep 28, 2020
2ea235c
Merge branch 'OTT-9' into ci
pm-viral-vala Sep 28, 2020
bc974f5
UOE-5616: Support wiid in pubmatic (#77)
PubMatic-OpenWrap Oct 7, 2020
d3c969e
OTT-29 Fixing Skip Dedup Map Issue
pm-viral-vala Oct 21, 2020
de219fb
OTT-29 Fixing Skip Dedup Map Issue
pm-viral-vala Oct 21, 2020
44b526e
OTT-29 Adding Video Duration in hb_pb_cat_dur key
pm-viral-vala Oct 26, 2020
ebce2fb
OTT-29 Adding Video Duration in hb_pb_cat_dur key
pm-viral-vala Oct 26, 2020
5f32d6d
UOE-5741: adding omitempty for ExtImpPrebid fields
pm-shalmali-patil Oct 28, 2020
76af1f0
UOE-5741: adding omitempty for ExtImpPrebid fields
pm-shalmali-patil Oct 28, 2020
2e3e1a7
OTT-9 Adding Duration in hb_pb_cat_dur field
pm-viral-vala Oct 28, 2020
344c965
OTT-9 Adding Duration in hb_pb_cat_dur field
pm-viral-vala Oct 28, 2020
466c9aa
UOE-5741: merging ci branch into UOE-5741
pm-shalmali-patil Nov 1, 2020
4f1e44a
OTT-45: Added logger and Prometheus metrics to capture bid.id collisi…
ShriprasadM Nov 3, 2020
34112cb
Merge branch 'ci' into OTT-9
pm-shriprasad-marathe Nov 3, 2020
5eb4968
OTT-9 Removed duplicate import
pm-shriprasad-marathe Nov 3, 2020
a1dfea5
OTT-45: corrected comment
pm-shriprasad-marathe Nov 3, 2020
fe90a71
OTT-9: Reverted with master changes. This changes are not required fo…
pm-shriprasad-marathe Nov 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions endpoints/openrtb2/ctv/response/adpod_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type highestCombination struct {
filteredBids map[string]*filteredBid
timeTakenCompExcl time.Duration // time taken by comp excl
timeTakenCombGen time.Duration // time taken by combination generator
nDealBids int
}

//AdPodGenerator AdPodGenerator
Expand Down Expand Up @@ -179,7 +180,7 @@ func (o *AdPodGenerator) getMaxAdPodBid(results []*highestCombination) *types.Ad
}
}

if len(result.bidIDs) > 0 && (nil == maxResult || maxResult.price < result.price) {
if len(result.bidIDs) > 0 && (nil == maxResult || maxResult.nDealBids < result.nDealBids || maxResult.price < result.price) {
maxResult = result
}
}
Expand Down Expand Up @@ -261,7 +262,7 @@ func findUniqueCombinations(data [][]*types.Bid, combination []int, maxCategoryS

ehc, inext, jnext, rc = evaluate(data[:], indices[:], totalBids, maxCategoryScore, maxDomainScore)
if nil != ehc {
if nil == hc || hc.price < ehc.price {
if nil == hc || hc.nDealBids < ehc.nDealBids || hc.price < ehc.price {
hc = ehc
} else {
// if you see current combination price lower than the highest one then break the loop
Expand Down Expand Up @@ -338,6 +339,7 @@ func evaluate(bids [][]*types.Bid, indices [][]int, totalBids int, maxCategorySc
price: 0,
categoryScore: make(map[string]int),
domainScore: make(map[string]int),
nDealBids: 0,
}
pos := 0

Expand All @@ -349,6 +351,11 @@ func evaluate(bids [][]*types.Bid, indices [][]int, totalBids int, maxCategorySc
hbc.bidIDs[pos] = bid.ID
pos++

//nDealBids
if bid.DealTierSatisfied {
hbc.nDealBids++
}

//Price
hbc.price = hbc.price + bid.Price

Expand Down
5 changes: 3 additions & 2 deletions endpoints/openrtb2/ctv/types/adpod_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import (
//Bid openrtb bid object with extra parameters
type Bid struct {
*openrtb.Bid
Duration int
FilterReasonCode constant.FilterReasonCode
Duration int
FilterReasonCode constant.FilterReasonCode
DealTierSatisfied bool
}

//ExtCTVBidResponse object for ctv bid resposne object
Expand Down
18 changes: 17 additions & 1 deletion endpoints/openrtb2/ctv/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/PubMatic-OpenWrap/prebid-server/endpoints/openrtb2/ctv/constant"
"github.com/PubMatic-OpenWrap/prebid-server/endpoints/openrtb2/ctv/types"
"github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext"
"github.com/golang/glog"
)

Expand All @@ -21,13 +22,28 @@ func GetDurationWiseBidsBucket(bids []*types.Bid) types.BidsBuckets {
}

for k, v := range result {
sort.Slice(v[:], func(i, j int) bool { return v[i].Price > v[j].Price })
//sort.Slice(v[:], func(i, j int) bool { return v[i].Price > v[j].Price })
sortBids(v[:])
result[k] = v
}

return result
}

func sortBids(bids []*types.Bid) {
sort.Slice(bids, func(i, j int) bool {
if bids[i].DealTierSatisfied == bids[j].DealTierSatisfied {
return bids[i].Price > bids[j].Price
}
return bids[i].DealTierSatisfied
})
}

// GetDealTierSatisfied ...
func GetDealTierSatisfied(ext *openrtb_ext.ExtBid) bool {
return ext != nil && ext.Prebid != nil && ext.Prebid.DealTierSatisfied
}

func DecodeImpressionID(id string) (string, int) {
index := strings.LastIndex(id, constant.CTVImpressionIDSeparator)
if index == -1 {
Expand Down
126 changes: 126 additions & 0 deletions endpoints/openrtb2/ctv/util/util_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package util

import (
"fmt"
"testing"

"github.com/PubMatic-OpenWrap/openrtb"

"github.com/PubMatic-OpenWrap/prebid-server/endpoints/openrtb2/ctv/types"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -48,3 +52,125 @@ func TestDecodeImpressionID(t *testing.T) {
})
}
}

func TestSortByDealPriority(t *testing.T) {

type testbid struct {
id string
price float64
isDealBid bool
}

testcases := []struct {
scenario string
bids []testbid
expectedBidIDOrdering []string
}{
/* tests based on truth table */
{
scenario: "all_deal_bids_do_price_based_sort",
bids: []testbid{
{id: "DB_$5", price: 5.0, isDealBid: true}, // Deal bid with low price
{id: "DB_$10", price: 10.0, isDealBid: true}, // Deal bid with high price
},
expectedBidIDOrdering: []string{"DB_$10", "DB_$5"}, // sort by price among deal bids
},
{
scenario: "normal_and_deal_bid_mix_case_1",
bids: []testbid{
{id: "DB_$15", price: 15.0, isDealBid: true}, // Deal bid with low price
{id: "B_$30", price: 30.0, isDealBid: false}, // Normal bid with high price
},
expectedBidIDOrdering: []string{"DB_$15", "B_$30"}, // no sort expected. Deal bid is already 1st in order
},
{
scenario: "normal_and_deal_bid_mix_case_2", // deal bids are not at start position in order
bids: []testbid{
{id: "B_$30", price: 30.0, isDealBid: false}, // Normal bid with high price
{id: "DB_$15", price: 15.0, isDealBid: true}, // Deal bid with low price
},
expectedBidIDOrdering: []string{"DB_$15", "B_$30"}, // sort based on deal bid
},
{
scenario: "all_normal_bids_sort_by_price_case_1",
bids: []testbid{
{id: "B_$5", price: 5.0, isDealBid: false},
{id: "B_$10", price: 10.0, isDealBid: false},
},
expectedBidIDOrdering: []string{"B_$10", "B_$5"}, // sort by price
},
{
scenario: "all_normal_bids_sort_by_price_case_2", // already sorted by highest price
bids: []testbid{
{id: "B_$10", price: 10.0, isDealBid: false},
{id: "B_$5", price: 5.0, isDealBid: false},
},
expectedBidIDOrdering: []string{"B_$10", "B_$5"}, // no sort required as already sorted
},
/* use cases */
{
scenario: "deal_bids_with_same_price",
bids: []testbid{
{id: "DB2_$10", price: 10.0, isDealBid: true},
{id: "DB1_$10", price: 10.0, isDealBid: true},
},
expectedBidIDOrdering: []string{"DB2_$10", "DB1_$10"}, // no sort expected
},
/* more than 2 Bids testcases */
{
scenario: "4_bids_with_first_and_last_are_deal_bids",
bids: []testbid{
{id: "DB_$15", price: 15.0, isDealBid: true}, // deal bid with low CPM than another bid
{id: "B_$40", price: 40.0, isDealBid: false}, // normal bid with highest CPM
{id: "B_$3", price: 3.0, isDealBid: false},
{id: "DB_$20", price: 20.0, isDealBid: true}, // deal bid with high cpm than another deal bid
},
expectedBidIDOrdering: []string{"DB_$20", "DB_$15", "B_$40", "B_$3"},
},
{
scenario: "deal_bids_and_normal_bids_with_same_price",
bids: []testbid{
{id: "B1_$7", price: 7.0, isDealBid: false},
{id: "DB2_$7", price: 7.0, isDealBid: true},
{id: "B3_$7", price: 7.0, isDealBid: false},
{id: "DB1_$7", price: 7.0, isDealBid: true},
{id: "B2_$7", price: 7.0, isDealBid: false},
},
expectedBidIDOrdering: []string{"DB2_$7", "DB1_$7", "B1_$7", "B3_$7", "B2_$7"}, // no sort expected
},
}

newBid := func(bid testbid) *types.Bid {
return &types.Bid{
Bid: &openrtb.Bid{
ID: bid.id,
Price: bid.price,
//Ext: json.RawMessage(`{"prebid":{ "dealTierSatisfied" : ` + bid.isDealBid + ` }}`),
},
DealTierSatisfied: bid.isDealBid,
}
}

for _, test := range testcases {
// if test.scenario != "deal_bids_and_normal_bids_with_same_price" {
// continue
// }
fmt.Println("Scenario : ", test.scenario)
bids := []*types.Bid{}
for _, bid := range test.bids {
bids = append(bids, newBid(bid))
}
for _, bid := range bids {
fmt.Println(bid.ID, ",", bid.Price, ",", bid.DealTierSatisfied)
}
sortBids(bids[:])
fmt.Println("After sort")
actual := []string{}
for _, bid := range bids {
fmt.Println(bid.ID, ",", bid.Price, ", ", bid.DealTierSatisfied)
actual = append(actual, bid.ID)
}
assert.Equal(t, test.expectedBidIDOrdering, actual, test.scenario+" failed")
fmt.Println("")
}
}
16 changes: 11 additions & 5 deletions endpoints/openrtb2/ctv_auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ func (deps *ctvEndpointDeps) CTVAuctionEndpoint(w http.ResponseWriter, r *http.R
}

response, err = deps.holdAuction(request, usersyncs)

ao.Request = request
ao.Response = response
if err != nil || nil == response {
Expand Down Expand Up @@ -546,6 +545,12 @@ func (deps *ctvEndpointDeps) getBids(resp *openrtb.BidResponse) {
}
vseat.Bid = append(vseat.Bid, *bid)
} else {
//reading extension, ingorning parsing error
ext := openrtb_ext.ExtBid{}
if nil != bid.Ext {
json.Unmarshal(bid.Ext, &ext)
}

//Adding adpod bids
impBids, ok := result[originalImpID]
if !ok {
Expand All @@ -560,9 +565,10 @@ func (deps *ctvEndpointDeps) getBids(resp *openrtb.BidResponse) {
bid.ID = util.GetUniqueBidID(bid.ID, len(impBids.Bids)+1)

impBids.Bids = append(impBids.Bids, &types.Bid{
Bid: bid,
FilterReasonCode: constant.CTVRCDidNotGetChance,
Duration: int(deps.impData[index].Config[sequenceNumber-1].MaxDuration),
Bid: bid,
FilterReasonCode: constant.CTVRCDidNotGetChance,
Duration: int(deps.impData[index].Config[sequenceNumber-1].MaxDuration),
DealTierSatisfied: util.GetDealTierSatisfied(&ext),
})
}
}
Expand Down Expand Up @@ -700,7 +706,7 @@ func (deps *ctvEndpointDeps) getBidResponseExt(resp *openrtb.BidResponse) (data
if nil != imp.Bid && len(imp.Bid.Bids) > 0 {
for _, bid := range imp.Bid.Bids {
//update adm
bid.AdM = constant.VASTDefaultTag
//bid.AdM = constant.VASTDefaultTag

//add duration value
raw, err := jsonparser.Set(bid.Ext, []byte(strconv.Itoa(int(bid.Duration))), "prebid", "video", "duration")
Expand Down
11 changes: 6 additions & 5 deletions exchange/bidder.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,12 @@ type adaptedBidder interface {
// pbsOrtbBid.bidVideo is optional but should be filled out by the Bidder if bidType is video.
// pbsOrtbBid.dealPriority will become "response.seatbid[i].bid.dealPriority" in the final OpenRTB response.
type pbsOrtbBid struct {
bid *openrtb.Bid
bidType openrtb_ext.BidType
bidTargets map[string]string
bidVideo *openrtb_ext.ExtBidPrebidVideo
dealPriority int
bid *openrtb.Bid
bidType openrtb_ext.BidType
bidTargets map[string]string
bidVideo *openrtb_ext.ExtBidPrebidVideo
dealPriority int
dealTierSatisfied bool
}

// pbsOrtbSeatBid is a SeatBid returned by an adaptedBidder.
Expand Down
Loading