-
Notifications
You must be signed in to change notification settings - Fork 769
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
Media type price granularity #2721
Changes from 7 commits
a96265f
61783cf
082ce54
0340ae9
59459f7
833547a
f19e70d
c28cf36
69dca07
cd013d8
f949e12
c0a3217
74af7d1
6183f53
ad16925
4ca20a4
23feb90
1cb8277
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1563,29 +1563,51 @@ func validateTargeting(t *openrtb_ext.ExtRequestTargeting) error { | |
} | ||
|
||
if t.PriceGranularity != nil { | ||
pg := t.PriceGranularity | ||
|
||
if pg.Precision == nil { | ||
return errors.New("Price granularity error: precision is required") | ||
} else if *pg.Precision < 0 { | ||
return errors.New("Price granularity error: precision must be non-negative") | ||
} else if *pg.Precision > openrtb_ext.MaxDecimalFigures { | ||
return fmt.Errorf("Price granularity error: precision of more than %d significant figures is not supported", openrtb_ext.MaxDecimalFigures) | ||
if err := validatePriceGranularity(t.PriceGranularity); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
var prevMax float64 = 0 | ||
for _, gr := range pg.Ranges { | ||
if gr.Max <= prevMax { | ||
return errors.New(`Price granularity error: range list must be ordered with increasing "max"`) | ||
if t.MediaTypePriceGranularity != nil { | ||
if t.MediaTypePriceGranularity.Video != nil { | ||
if err := validatePriceGranularity(t.MediaTypePriceGranularity.Video); err != nil { | ||
return err | ||
} | ||
|
||
if gr.Increment <= 0.0 { | ||
return errors.New("Price granularity error: increment must be a nonzero positive number") | ||
} | ||
if t.MediaTypePriceGranularity.Banner != nil { | ||
if err := validatePriceGranularity(t.MediaTypePriceGranularity.Banner); err != nil { | ||
return err | ||
} | ||
} | ||
if t.MediaTypePriceGranularity.Native != nil { | ||
if err := validatePriceGranularity(t.MediaTypePriceGranularity.Native); err != nil { | ||
return err | ||
} | ||
prevMax = gr.Max | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func validatePriceGranularity(pg *openrtb_ext.PriceGranularity) error { | ||
if pg.Precision == nil { | ||
return errors.New("Price granularity error: precision is required") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will we ever get to a situation where this will be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Discussed offline. No changes needed. |
||
} else if *pg.Precision < 0 { | ||
return errors.New("Price granularity error: precision must be non-negative") | ||
} else if *pg.Precision > openrtb_ext.MaxDecimalFigures { | ||
return fmt.Errorf("Price granularity error: precision of more than %d significant figures is not supported", openrtb_ext.MaxDecimalFigures) | ||
} | ||
|
||
var prevMax float64 = 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a Go preference for how this should be initialized? e.g: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code was moved without modifications from |
||
for _, gr := range pg.Ranges { | ||
if gr.Max <= prevMax { | ||
return errors.New(`Price granularity error: range list must be ordered with increasing "max"`) | ||
} | ||
|
||
if gr.Increment <= 0.0 { | ||
return errors.New("Price granularity error: increment must be a nonzero positive number") | ||
} | ||
prevMax = gr.Max | ||
} | ||
return nil | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1890,6 +1890,180 @@ func TestValidateTargeting(t *testing.T) { | |
}, | ||
expectedError: errors.New("Price granularity error: increment must be a nonzero positive number"), | ||
}, | ||
{ | ||
name: "media type price granularity video correct", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that we have separated out price granularity validation in a separate method, you think we can add another unit test specifically for logic in This allows detailed test cases to be separated out from higher level function test cases. Plus, the test cases in this test have become too large and this will help keep them to a manageable number as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! Some tests are gone due to duplicated tests with |
||
givenTargeting: &openrtb_ext.ExtRequestTargeting{ | ||
IncludeWinners: ptrutil.ToPtr(true), | ||
MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ | ||
Video: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 10.0, Increment: 1}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expectedError: nil, | ||
}, | ||
{ | ||
name: "media type price granularity banner correct", | ||
givenTargeting: &openrtb_ext.ExtRequestTargeting{ | ||
IncludeWinners: ptrutil.ToPtr(true), | ||
MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ | ||
Banner: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 10.0, Increment: 1}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expectedError: nil, | ||
}, | ||
{ | ||
name: "media type price granularity native correct", | ||
givenTargeting: &openrtb_ext.ExtRequestTargeting{ | ||
IncludeWinners: ptrutil.ToPtr(true), | ||
MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ | ||
Native: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 20.0, Increment: 1}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expectedError: nil, | ||
}, | ||
{ | ||
name: "media type price granularity video and banner correct", | ||
givenTargeting: &openrtb_ext.ExtRequestTargeting{ | ||
IncludeWinners: ptrutil.ToPtr(true), | ||
MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ | ||
Banner: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 10.0, Increment: 1}, | ||
}, | ||
}, | ||
Video: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 10.0, Increment: 1}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expectedError: nil, | ||
}, | ||
{ | ||
name: "media type price granularity video and native correct", | ||
givenTargeting: &openrtb_ext.ExtRequestTargeting{ | ||
IncludeWinners: ptrutil.ToPtr(true), | ||
MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ | ||
Native: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 30.0, Increment: 1}, | ||
}, | ||
}, | ||
Video: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 10.0, Increment: 1}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expectedError: nil, | ||
}, | ||
{ | ||
name: "media type price granularity video incorrect", | ||
givenTargeting: &openrtb_ext.ExtRequestTargeting{ | ||
IncludeWinners: ptrutil.ToPtr(true), | ||
MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ | ||
Video: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 10.0, Increment: -1}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expectedError: errors.New("Price granularity error: increment must be a nonzero positive number"), | ||
}, | ||
{ | ||
name: "media type price granularity banner incorrect", | ||
givenTargeting: &openrtb_ext.ExtRequestTargeting{ | ||
IncludeWinners: ptrutil.ToPtr(true), | ||
MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ | ||
Banner: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 0.0, Increment: 1}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expectedError: errors.New("Price granularity error: range list must be ordered with increasing \"max\""), | ||
}, | ||
{ | ||
name: "media type price granularity native incorrect", | ||
givenTargeting: &openrtb_ext.ExtRequestTargeting{ | ||
IncludeWinners: ptrutil.ToPtr(true), | ||
MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ | ||
Native: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 0.0, Increment: 1}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expectedError: errors.New("Price granularity error: range list must be ordered with increasing \"max\""), | ||
}, | ||
{ | ||
name: "media type price granularity video correct and banner incorrect", | ||
givenTargeting: &openrtb_ext.ExtRequestTargeting{ | ||
IncludeWinners: ptrutil.ToPtr(true), | ||
MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ | ||
Banner: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 10.0, Increment: -1}, | ||
}, | ||
}, | ||
Video: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 0.0, Increment: 1}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expectedError: errors.New("Price granularity error: range list must be ordered with increasing \"max\""), | ||
}, | ||
{ | ||
name: "media type price granularity native incorrect and banner correct", | ||
givenTargeting: &openrtb_ext.ExtRequestTargeting{ | ||
IncludeWinners: ptrutil.ToPtr(true), | ||
MediaTypePriceGranularity: &openrtb_ext.MediaTypePriceGranularity{ | ||
Native: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 10.0, Increment: -1}, | ||
}, | ||
}, | ||
Video: &openrtb_ext.PriceGranularity{ | ||
Precision: ptrutil.ToPtr(2), | ||
Ranges: []openrtb_ext.GranularityRange{ | ||
{Min: 0.0, Max: 0.0, Increment: 1}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expectedError: errors.New("Price granularity error: range list must be ordered with increasing \"max\""), | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -185,12 +185,12 @@ func (a *auction) validateAndUpdateMultiBid(adapterBids map[openrtb_ext.BidderNa | |
} | ||
} | ||
|
||
func (a *auction) setRoundedPrices(priceGranularity openrtb_ext.PriceGranularity) { | ||
func (a *auction) setRoundedPrices(targetingData *targetData) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can make the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed |
||
roundedPrices := make(map[*entities.PbsOrtbBid]string, 5*len(a.winningBids)) | ||
for _, topBidsPerImp := range a.winningBidsByBidder { | ||
for _, topBidsPerBidder := range topBidsPerImp { | ||
for _, topBid := range topBidsPerBidder { | ||
roundedPrices[topBid] = GetPriceBucket(topBid.Bid.Price, priceGranularity) | ||
roundedPrices[topBid] = GetPriceBucket(*topBid.Bid, targetingData) | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -390,7 +390,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog * | |
// A non-nil auction is only needed if targeting is active. (It is used below this block to extract cache keys) | ||
auc = newAuction(adapterBids, len(r.BidRequestWrapper.Imp), targData.preferDeals) | ||
auc.validateAndUpdateMultiBid(adapterBids, targData.preferDeals, r.Account.DefaultBidLimit) | ||
auc.setRoundedPrices(targData.priceGranularity) | ||
auc.setRoundedPrices(targData) | ||
|
||
if requestExtPrebid.SupportDeals { | ||
dealErrs := applyDealSupport(r.BidRequestWrapper.BidRequest, auc, bidCategory, multiBidMap) | ||
|
@@ -908,7 +908,7 @@ func applyCategoryMapping(ctx context.Context, targeting openrtb_ext.ExtRequestT | |
bidID := bid.Bid.ID | ||
var duration int | ||
var category string | ||
var pb string | ||
var priceBucket string | ||
|
||
if bid.BidVideo != nil { | ||
duration = bid.BidVideo.Duration | ||
|
@@ -942,7 +942,7 @@ func applyCategoryMapping(ctx context.Context, targeting openrtb_ext.ExtRequestT | |
|
||
// TODO: consider should we remove bids with zero duration here? | ||
|
||
pb = GetPriceBucket(bid.Bid.Price, targData.priceGranularity) | ||
priceBucket = GetPriceBucket(*bid.Bid, targData) | ||
|
||
newDur := duration | ||
if len(targeting.DurationRangeSec) > 0 { | ||
|
@@ -967,10 +967,10 @@ func applyCategoryMapping(ctx context.Context, targeting openrtb_ext.ExtRequestT | |
var categoryDuration string | ||
var dupeKey string | ||
if brandCatExt.WithCategory { | ||
categoryDuration = fmt.Sprintf("%s_%s_%ds", pb, category, newDur) | ||
categoryDuration = fmt.Sprintf("%s_%s_%ds", priceBucket, category, newDur) | ||
dupeKey = category | ||
} else { | ||
categoryDuration = fmt.Sprintf("%s_%ds", pb, newDur) | ||
categoryDuration = fmt.Sprintf("%s_%ds", priceBucket, newDur) | ||
dupeKey = categoryDuration | ||
} | ||
|
||
|
@@ -984,7 +984,7 @@ func applyCategoryMapping(ctx context.Context, targeting openrtb_ext.ExtRequestT | |
if err != nil { | ||
dupeBidPrice = 0 | ||
} | ||
currBidPrice, err := strconv.ParseFloat(pb, 64) | ||
currBidPrice, err := strconv.ParseFloat(priceBucket, 64) | ||
if err != nil { | ||
currBidPrice = 0 | ||
} | ||
|
@@ -1025,7 +1025,7 @@ func applyCategoryMapping(ctx context.Context, targeting openrtb_ext.ExtRequestT | |
} | ||
} | ||
res[bidID] = categoryDuration | ||
dedupe[dupeKey] = bidDedupe{bidderName: bidderName, bidIndex: bidInd, bidID: bidID, bidPrice: pb} | ||
dedupe[dupeKey] = bidDedupe{bidderName: bidderName, bidIndex: bidInd, bidID: bidID, bidPrice: priceBucket} | ||
} | ||
|
||
if len(bidsToRemove) > 0 { | ||
|
@@ -1349,20 +1349,6 @@ func listBiddersWithRequests(bidderRequests []BidderRequest) []openrtb_ext.Bidde | |
return liveAdapters | ||
} | ||
|
||
func getPrebidMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { | ||
if bid.Ext != nil { | ||
var bidExt openrtb_ext.ExtBid | ||
err := json.Unmarshal(bid.Ext, &bidExt) | ||
if err == nil && bidExt.Prebid != nil { | ||
return openrtb_ext.ParseBidType(string(bidExt.Prebid.Type)) | ||
} | ||
} | ||
|
||
return "", &errortypes.BadServerResponse{ | ||
Message: fmt.Sprintf("Failed to parse bid mediatype for impression \"%s\"", bid.ImpID), | ||
} | ||
} | ||
|
||
func buildStoredAuctionResponse(storedAuctionResponses map[string]json.RawMessage) ( | ||
map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, | ||
*openrtb_ext.Fledge, | ||
|
@@ -1383,28 +1369,10 @@ func buildStoredAuctionResponse(storedAuctionResponses map[string]json.RawMessag | |
//set imp id from request | ||
for i := range seat.Bid { | ||
seat.Bid[i].ImpID = impId | ||
mType := seat.Bid[i].MType | ||
var bidType openrtb_ext.BidType | ||
if mType > 0 { | ||
switch mType { | ||
case openrtb2.MarkupBanner: | ||
bidType = openrtb_ext.BidTypeBanner | ||
case openrtb2.MarkupVideo: | ||
bidType = openrtb_ext.BidTypeVideo | ||
case openrtb2.MarkupAudio: | ||
bidType = openrtb_ext.BidTypeAudio | ||
case openrtb2.MarkupNative: | ||
bidType = openrtb_ext.BidTypeNative | ||
default: | ||
return nil, nil, nil, &errortypes.BadServerResponse{ | ||
Message: fmt.Sprintf("Failed to parse bid mType for impression \"%s\"", seat.Bid[i].ImpID), | ||
} | ||
} | ||
} else { | ||
var err error | ||
bidType, err = getPrebidMediaTypeForBid(seat.Bid[i]) | ||
if err != nil { | ||
return nil, nil, nil, err | ||
bidType, err := getMediaTypeForBid(seat.Bid[i]) | ||
if err != nil { | ||
return nil, nil, nil, &errortypes.BadServerResponse{ | ||
Message: fmt.Sprintf("Failed to parse bid mType for impression \"%s\"", seat.Bid[i].ImpID), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest we move this error message inside There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see your idea. I tried it locally and find out we need to intercept a final error and replace error message. What do you think? |
||
} | ||
} | ||
bidsToAdd = append(bidsToAdd, &entities.PbsOrtbBid{Bid: &seat.Bid[i], BidType: bidType}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For my own understanding, why do we not need to have checks for other media types like
Native
andAudio
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is a question I wanted to ask too.
Here is a link to documentation: https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-auction.html#targeting
In the table of attributes I only found
mediatypepricegranularity.banner
andmediatypepricegranularity.video
. I'll confirm this with the team.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bretg Bret, can you please confirm we only want to support
banner
andvideo
for media type price granularity.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
native should be supported as defined in the original issue #857. Doc updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added