comparison channel/ad_channel.inc @ 7:6aeff3329e01 ad

maj module ad 2.1rc1
author piotre
date Mon, 20 Jul 2009 13:54:40 +0000
parents e5584a19768b
children
comparison
equal deleted inserted replaced
6:b7653861e0b4 7:6aeff3329e01
12 * Filter advertisements not in an appropriate channel, from cache. 12 * Filter advertisements not in an appropriate channel, from cache.
13 */ 13 */
14 function ad_channel_cache_filter($ads) { 14 function ad_channel_cache_filter($ads) {
15 _debug_echo("ad_channel_cache: adserve_cache_filter"); 15 _debug_echo("ad_channel_cache: adserve_cache_filter");
16 16
17 // get channel array from cache
17 $channels = adserve_cache('get_cache', 'channel'); 18 $channels = adserve_cache('get_cache', 'channel');
18 $valid = array(); 19 // 0 = only display advertisements not assigned to any channel if no matching
19 $nochannel = array(); 20 // ads in selected channel; 1 = always display advertisements not assigned to
20 $nochannel_weight = array(); 21 // any channel; 2 = never display advertisements not assigned to any channel
21 $matched_channel = array(); 22 $nochannel_display = $channels['display'];
23 $valid_ads = array();
24 $nochannel_fallback_ads = array();
25 $nochannel_percent = array();
26 // determine which channels each advertisement is assigned to
22 foreach ($ads as $aid) { 27 foreach ($ads as $aid) {
23 _debug_echo("ad_channel_cache: checking aid($aid)"); 28 _debug_echo("ad_channel_cache: checking aid($aid)");
24 if (is_array($channels['ads']) && isset($channels['ads'][$aid]) && 29 if (is_array($channels['ads']) && isset($channels['ads'][$aid]) &&
25 is_array($channels['ads'][$aid])) { 30 is_array($channels['ads'][$aid])) {
26 foreach ($channels['ads'][$aid] as $chid) { 31 foreach ($channels['ads'][$aid] as $chid) {
27 $channel = $channels['channels'][$chid]; 32 $channel = $channels['channels'][$chid];
28 $display = $channel->display; 33 $display_by_url = $channel->display;
29 $urls = unserialize($channel->urls); 34 $urls = unserialize($channel->urls);
30 $frontpage = adserve_variable('site_frontpage') ? adserve_variable('site_frontpage') : 'node'; 35 $frontpage = adserve_variable('site_frontpage') ? adserve_variable('site_frontpage') : 'node';
31 $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1'. preg_quote($frontpage, '/') .'\2'), preg_quote($urls, '/')) .')$/'; 36 $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1'. preg_quote($frontpage, '/') .'\2'), preg_quote($urls, '/')) .')$/';
32 $match = preg_match($regexp, adserve_variable('url')); 37 $match = preg_match($regexp, adserve_variable('url'));
33 _debug_echo("ad_channel_cache: checking aid($aid) against channel($chid) path(". adserve_variable('url') .") regexp($regexp) match($match)"); 38 _debug_echo("ad_channel_cache: checking aid($aid) against channel($chid) path(". adserve_variable('url') .") regexp($regexp) match($match) display[$display_by_url]");
34 if ($display == 0) { // display on all except listed urls 39 // display ad on all except matching urls
40 if ($display_by_url == 0) {
35 if (empty($urls) || !$match) { 41 if (empty($urls) || !$match) {
36 _debug_echo("ad_channel_cache: aid($aid) is valid"); 42 _debug_echo("ad_channel_cache: aid($aid) is valid");
37 $valid[] = $aid; 43 $valid_ads[$chid][] = $aid;
38 if ($display == 1) { 44 if ($nochannel_display == 1) {
39 $nochannel_weight[$chid] = $channel->no_channel_weight; 45 $nochannel_percent[$chid] = $channel->no_channel_percent;
40 } 46 _debug_echo("ad_channel_cache: channel($chid) no_channel_percent($nochannel_percent[$chid])");
41 $matched_channel[$aid] = $chid; 47 }
42 _debug_echo("ad_channel_cache: channel($channel->chid) no_channel_weight(". $nochannel_weight[$chid] .')'); 48 else {
49 _debug_echo("ad_channel_cache: channel($chid)");
50 }
43 break; 51 break;
44 } 52 }
45 } 53 }
46 else { // display only on listed urls 54 // display ad on matching urls
55 else {
47 if (!empty($urls) && $match) { 56 if (!empty($urls) && $match) {
48 _debug_echo("ad_channel_cache: aid($aid) is valid"); 57 _debug_echo("ad_channel_cache: aid($aid) is valid");
49 $valid[] = $aid; 58 $valid_ads[$chid][] = $aid;
50 if ($display == 1) { 59 if ($nochannel_display == 1) {
51 $nochannel_weight[$chid] = $channel->no_channel_weight; 60 $nochannel_percent[$chid] = $channel->no_channel_percent;
52 } 61 _debug_echo("ad_channel_cache: channel($chid) no_channel_percent($nochannel_percent[$chid])");
53 $matched_channel[$aid] = $chid; 62 }
63 else {
64 _debug_echo("ad_channel_cache: channel($chid)");
65 }
54 break; 66 break;
55 } 67 }
56 } 68 }
69 // no match so we didn't hit a break, ad is not valid
57 _debug_echo("ad_channel_cache: aid($aid) is not valid"); 70 _debug_echo("ad_channel_cache: aid($aid) is not valid");
58 } 71 }
59 } 72 }
60 else { 73 else {
61 // no channel information for ad, it's valid 74 // no channel information for ad
62 $display = $channels['display']; 75 _debug_echo("ad_channel_cache: aid($aid) has no channel info, nochannel_display($nochannel_display)");
63 _debug_echo("ad_channel_cache: aid($aid) has no channel info [$display]"); 76 switch ($nochannel_display) {
64 switch ($display) {
65 case 0: 77 case 0:
66 $nochannel[] = $aid; 78 $nochannel_fallback_ads[] = $aid;
67 _debug_echo("ad_channel_cache: aid($aid) is valid if no valid ads found in current channel"); 79 _debug_echo("ad_channel_cache: non-channel aid($aid) is valid if no valid ads are assigned to current channel");
68 break; 80 break;
69 case 1: 81 case 1:
70 $valid[] = $aid; 82 $valid_ads[0][] = $aid;
71 _debug_echo("ad_channel_cache: aid($aid) is valid"); 83 _debug_echo("ad_channel_cache: non-channel aid($aid) is valid");
72 break; 84 break;
73 case 2: 85 case 2:
74 _debug_echo("ad_channel_cache: aid($aid) is not valid"); 86 _debug_echo("ad_channel_cache: aid($aid) is not valid");
75 break; 87 break;
76 } 88 }
77 } 89 }
78 } 90 }
79 91
80 // Apply weights, applicable for advertisements that are not assigned to any 92 // Apply frequencies, applicable to all channels
81 // channel. 93 if (!empty($valid_ads) && !empty($nochannel_percent)) {
82 if (!empty($valid) && !empty($nochannel_weight)) { 94 $frequencies = array();
83 $weights = array(); 95 foreach (array_keys($valid_ads) as $chid) {
84 foreach ($valid as $aid) { 96 if ($chid) {
85 if (isset($matched_channel[$aid])) { 97 if (isset($nochannel_percent[$chid]) && $nochannel_percent[$chid]) {
86 $chid = $matched_channel[$aid]; 98 $frequencies[$chid] = $nochannel_percent[$chid];
87 if (isset($nochannel_weight[$chid])) { 99 _debug_echo("ad_channel_cache: channel $chid has a non-channel ad frequency of ". $nochannel_percent[$chid]."%");
88 $weights[$aid] = $nochannel_weight[$chid];
89 _debug_echo("ad_channel_cache: ad $aid in channel $chid with weight ". $nochannel_weight[$chid]);
90 } 100 }
91 else { 101 else {
92 // by default, ads are assigned a weight of 100% 102 // by default, channels return 'non-channel ads' with a frequency
93 $weights[$aid] = 100; 103 // of 10%
94 _debug_echo("ad_channel_cache: ad $aid in channel $chid with default weight of 100"); 104 $frequencies[$chid] = 10;
105 _debug_echo("ad_channel_cache: channel $chid assigned a default non-channel ad frequency of 10%");
95 } 106 }
96 } 107 }
97 else { 108 else {
98 // by default, ads are assigned a weight of 100% 109 // frequency for non-channel ads is not meaningful
99 $weights[$aid] = 100; 110 }
100 _debug_echo("ad_channel_cache: ad $aid in no channel with default weight of 100"); 111 }
101 } 112 if (!empty($frequencies)) {
102 } 113 $balanced_channels = array();
103 if (!empty($weights)) { 114 $num_channels = sizeof($valid_ads);
104 $gcd = ad_channel_probability_gcd($weights); 115
105 $display = array(); 116 foreach (array_keys($valid_ads) as $chid) {
106 _debug_echo("ad_channel_cache: adjusting channel weights, gcd ($gcd)"); 117 if (isset($frequencies[$chid])) {
107 foreach ($valid as $aid) { 118 // for a given channel, ensure the proper ratio to non-channel ads
108 $weight = $weights[$aid] / $gcd; 119 if ($frequencies[$chid] <= 50) { // increase occurrences of $chid
109 for ($i = 1; $i <= $weight; $i++) { 120 $balanced_channels[] = 0;
110 $display[] = $aid; 121 $frequency = round(100 / $frequencies[$chid]) - 1;
111 } 122 _debug_echo("ad_channel_cache: adjusting ratio of channel($chid) to $frequency:1 relative non-channel ads");
112 } 123 for ($i = 1; $i <= $frequency; $i++) {
113 $valid = $display; 124 $balanced_channels[] = $chid;
114 } 125 }
115 } 126 }
116 else if (empty($valid) && !empty($nochannel)) { 127 else { // add $chid and additional non-channel ads
128 $balanced_channels[] = $chid;
129 $frequency = round(100 / (100 - $frequencies[$chid])) - 1;
130 _debug_echo("ad_channel_cache: adjusting ratio of channel($chid) to 1:$frequency relative non-channel ads");
131 for ($i = 1; $i <= $frequency; $i++) {
132 $balanced_channels[] = 0;
133 }
134 }
135 }
136 }
137 _debug_echo('ad_channel_cache: channel 0 contains all non-channel ads');
138 if (adserve_variable('debug') >= 2) {
139 foreach ($balanced_channels as $key => $chid) {
140 _debug_echo("ad_channel_cache: channel $chid => index $key");
141 }
142 }
143 }
144 $random_channel = _select_channel_id($balanced_channels);
145 }
146 else if (!empty($valid_ads)) {
147 foreach ($valid_ads as $chid => $ads) {
148 $chids[$chid] = $chid;
149 }
150 shuffle($chids);
151 $random_channel = array_pop($chids);
152 }
153 else if (empty($valid_ads) && !empty($nochannel_fallback_ads)) {
117 _debug_echo("ad_channel_cache: using ads with no channel info"); 154 _debug_echo("ad_channel_cache: using ads with no channel info");
118 $valid = $nochannel; 155 $valid_ads[0] = $nochannel_fallback_ads;
156 $random_channel = 0;
119 } 157 }
120 158
121 $premiere = adserve_cache('get_cache', 'premiere'); 159 $premiere = adserve_cache('get_cache', 'premiere');
122 $premieres = array();
123 if (is_array($premiere)) { 160 if (is_array($premiere)) {
124 foreach ($valid as $aid) { 161 $premieres = array();
125 if (in_array($aid, $premiere)) { 162 foreach (array_keys($valid_ads) as $chid) {
126 _debug_echo("ad_channel_cache: aid($aid) is premiere advertisement"); 163 foreach ($valid_ads[$chid] as $aid) {
127 $premieres[$aid] = $aid; 164 if (in_array($aid, $premiere)) {
128 } 165 _debug_echo("ad_channel_cache: aid($aid) is premiere advertisement");
129 else { 166 $premieres[$aid] = $aid;
130 _debug_echo("ad_channel_cache: aid($aid) is not a premiere advertisement"); 167 }
168 else {
169 _debug_echo("ad_channel_cache: aid($aid) is not a premiere advertisement");
170 }
131 } 171 }
132 } 172 }
133 if (!empty($premieres)) { 173 if (!empty($premieres)) {
134 _debug_echo("ad_channel_cache: returning premiere advertisements"); 174 _debug_echo("ad_channel_cache: returning premiere advertisements");
135 return $premieres; 175 return $premieres;
136 } 176 }
137 } 177 }
138 _debug_echo("ad_channel_cache: returning non-premiere advertisements"); 178 _debug_echo("ad_channel_cache: returning non-premiere advertisements from randomly selected channel $random_channel");
139 return $valid; 179
180 if (isset($valid_ads[$random_channel])) {
181 return ad_channel_enforce_inventory_level($random_channel, $valid_ads[$random_channel]);
182 }
140 } 183 }
141 184
142 /** 185 /**
143 * Returns the greatest common divisor of an array of integers. 186 * Randomly select a valid channel id from an array channel ids
144 */ 187 * @param array, valid array.
145 function ad_channel_probability_gcd($integers) { 188 */
146 $gcd = array_shift($integers); 189 function _select_channel_id($choices) {
147 190 $selected = 0;
148 while (!empty($integers)) { 191 if (is_array($choices)) {
149 $gcd = _ad_channel_probability_gcd($gcd, array_shift($integers)); 192 $available = sizeof($choices);
150 } 193 _debug_echo("ad_channel_cache: randomly selecting from $available indexes.");
151 return $gcd; 194 $selected = $available > 1 ? $choices[mt_rand(0, $available - 1)] : $choices[0];
195 _debug_echo("ad_channel_cache: randomly selected channel $selected.");
196 }
197
198 return $selected;
152 } 199 }
153 200
154 /** 201 /*
155 * Helper function to calculate the greatest common divisor using the Euclidean 202 * Augment the selected channel with 'remnant' ads to ensure that any specified
156 * algorithm (http://en.wikipedia.org/wiki/Euclidean_algorithm). 203 * inventory level is honored
157 */ 204 * @param int, channel id
158 function _ad_channel_probability_gcd($a, $b) { 205 * @param array, valid array.
159 if ($b == 0) { 206 */
160 return $a; 207 function ad_channel_enforce_inventory_level($chid, $ads) {
208 if ($chid > 0) {
209 $channels = adserve_cache('get_cache', 'channel');
210 $channel = $channels['channels'][$chid];
211 $level = $channel->inventory;
212 $num_ads = count($ads);
213 if ($num_ads < $level) {
214 _debug_echo("ad_channel_enforce_inventory_level: channel($chid) has $num_ads and needs $level");
215 $remnants = array_values(adserve_cache('get_cache', 'remnant'));
216 $available = count($remnants);
217 if ($available > 0) {
218 _debug_echo("ad_channel_enforce_inventory_level: randomly selecting from $available remnants.");
219 while (count($ads) < $level) {
220 shuffle($remnants);
221 $selected = array_pop($remnants);
222 _debug_echo("ad_channel_enforce_inventory_level: selected $selected.");
223 $ads[] = $selected;
224 }
225 }
226 else {
227 _debug_echo("ad_channel_enforce_inventory_level: no remnants to choose from.");
228 }
229 }
230 else {
231 _debug_echo("ad_channel_enforce_inventory_level: channel($chid) no inventory level assigned");
232 }
161 } 233 }
162 else { 234 else {
163 return _ad_channel_probability_gcd($b, $a % $b); 235 _debug_echo("ad_channel_enforce_inventory_level: not needed for channel($chid)");
164 } 236 }
237 return $ads;
165 } 238 }
239