Skip to content

Commit

Permalink
Adding freewheel module (prebid#3489)
Browse files Browse the repository at this point in the history
* adding freewheel module

* adding commented references to adpod module

* upadate some properties and refactor

* updated adpod import and competitive exclusion logic

* add hook to freewheel module

* add logic for deferCaching

* refactor targeting logic for deferCaching and activate commented code

* modify import for test file

* update hook
  • Loading branch information
jaiminpanchal27 authored Feb 27, 2019
1 parent e96d58e commit d4ff1ec
Show file tree
Hide file tree
Showing 2 changed files with 425 additions and 0 deletions.
166 changes: 166 additions & 0 deletions modules/freeWheelAdserverVideo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/**
* This module adds Freewheel support for Video to Prebid.
*/

import { registerVideoSupport } from '../src/adServerManager';
import { auctionManager } from '../src/auctionManager';
import { groupBy, deepAccess, logError } from '../src/utils';
import { config } from '../src/config';
import { ADPOD } from '../src/mediaTypes';
import { initAdpodHooks, TARGETING_KEY_PB_CAT_DUR, TARGETING_KEY_CACHE_ID, callPrebidCacheAfterAuction } from './adpod';
import { getHook } from '../src/hook';

export function notifyTranslationModule(fn) {
fn.call(this, 'freewheel');
}

getHook('registerAdserver').before(notifyTranslationModule);

/**
* This function returns targeting keyvalue pairs for freewheel adserver module
* @param {Object} options
* @param {Array[string]} codes
* @param {function} callback
* @returns targeting kvs for adUnitCodes
*/
export function getTargeting({codes, callback} = {}) {
if (!callback) {
logError('No callback function was defined in the getTargeting call. Aborting getTargeting().');
return;
}
codes = codes || [];
const adPodAdUnits = getAdPodAdUnits(codes);
const bidsReceived = auctionManager.getBidsReceived();
const competiveExclusionEnabled = config.getConfig('adpod.brandCategoryExclusion');
const deferCachingSetting = config.getConfig('adpod.deferCaching');
const deferCachingEnabled = (typeof deferCachingSetting === 'boolean') ? deferCachingSetting : true;

let bids = getBidsForAdpod(bidsReceived, adPodAdUnits);
bids = (competiveExclusionEnabled || deferCachingEnabled) ? getExclusiveBids(bids) : bids;
bids.sort(compareOn('cpm'));

let targeting = {};
if (deferCachingEnabled === false) {
adPodAdUnits.forEach((adUnit) => {
let adPodTargeting = [];
let adPodDurationSeconds = deepAccess(adUnit, 'mediaTypes.video.adPodDurationSec');

bids
.filter((bid) => bid.adUnitCode === adUnit.code)
.forEach((bid, index, arr) => {
if (bid.video.durationBucket <= adPodDurationSeconds) {
adPodTargeting.push({
[TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR]
});
adPodDurationSeconds -= bid.video.durationBucket;
}
if (index === arr.length - 1 && adPodTargeting.length > 0) {
adPodTargeting.push({
[TARGETING_KEY_CACHE_ID]: bid.adserverTargeting[TARGETING_KEY_CACHE_ID]
});
}
});
targeting[adUnit.code] = adPodTargeting;
});

callback(null, targeting);
} else {
let bidsToCache = [];
adPodAdUnits.forEach((adUnit) => {
let adPodDurationSeconds = deepAccess(adUnit, 'mediaTypes.video.adPodDurationSec');

bids
.filter((bid) => bid.adUnitCode === adUnit.code)
.forEach((bid) => {
if (bid.video.durationBucket <= adPodDurationSeconds) {
bidsToCache.push(bid);
adPodDurationSeconds -= bid.video.durationBucket;
}
});
});

callPrebidCacheAfterAuction(bidsToCache, function(error, bidsSuccessfullyCached) {
if (error) {
callback(error, null);
} else {
let groupedBids = groupBy(bidsSuccessfullyCached, 'adUnitCode');
Object.keys(groupedBids).forEach((adUnitCode) => {
let adPodTargeting = [];

groupedBids[adUnitCode].forEach((bid, index, arr) => {
adPodTargeting.push({
[TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR]
});

if (index === arr.length - 1 && adPodTargeting.length > 0) {
adPodTargeting.push({
[TARGETING_KEY_CACHE_ID]: bid.adserverTargeting[TARGETING_KEY_CACHE_ID]
});
}
});
targeting[adUnitCode] = adPodTargeting;
});

callback(null, targeting);
}
});
}
return targeting;
}

/**
* This function returns the adunit of mediaType adpod
* @param {Array} codes adUnitCodes
* @returns {Array[Object]} adunits of mediaType adpod
*/
function getAdPodAdUnits(codes) {
return auctionManager.getAdUnits()
.filter((adUnit) => deepAccess(adUnit, 'mediaTypes.video.context') === ADPOD)
.filter((adUnit) => (codes.length > 0) ? codes.indexOf(adUnit.code) != -1 : true);
}

function compareOn(property) {
return function compare(a, b) {
if (a[property] < b[property]) {
return 1;
}
if (a[property] > b[property]) {
return -1;
}
return 0;
}
}

/**
* This function removes bids of same freewheel category. It will be used when competitive exclusion is enabled.
* @param {Array[Object]} bidsReceived
* @returns {Array[Object]} unique freewheel category bids
*/
function getExclusiveBids(bidsReceived) {
let bids = bidsReceived
.map((bid) => Object.assign({}, bid, {[TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR]}));
bids = groupBy(bids, TARGETING_KEY_PB_CAT_DUR);
let filteredBids = [];
Object.keys(bids).forEach((targetingKey) => {
bids[targetingKey].sort(compareOn('responseTimestamp'));
filteredBids.push(bids[targetingKey][0]);
});
return filteredBids;
}

/**
* This function returns bids for adpod adunits
* @param {Array[Object]} bidsReceived
* @param {Array[Object]} adPodAdUnits
* @returns {Array[Object]} bids of mediaType adpod
*/
function getBidsForAdpod(bidsReceived, adPodAdUnits) {
let adUnitCodes = adPodAdUnits.map((adUnit) => adUnit.code);
return bidsReceived
.filter((bid) => adUnitCodes.indexOf(bid.adUnitCode) != -1 && (bid.video && bid.video.context === ADPOD))
}

initAdpodHooks();
registerVideoSupport('freewheel', {
getTargeting: getTargeting
});
Loading

0 comments on commit d4ff1ec

Please sign in to comment.