/** * A private method that calcualtes the ZIF value(s) for a given zone. * * For each operation interval that requires the zone's ZIF value to be updated, * the ZIF value for the zone is calculated via the following algorithm: * * - If the zone has been operational for at least ZONE_FORECAST_BASELINE_WEEKS weeks * (i.e. the zone has actual impressions for the past ZONE_FORECAST_BASELINE_WEEKS * occurrences of the same operation interval as is currently being updated), then * the expected impressions value is the average of the past two operation intervals' * actual impressions of the zone, multiplied by a moving average trend value. * - Else, if the zone has not been operational for at least * ZONE_FORECAST_BASELINE_WEEKS weeks, then the expected impressions is set to the * actual number of impressions in the previous operation interval for that zone. * - Else the previous operation interval for that zone does not have an actual * number of impressions, then the expected number of impressions for that * zone is set to ZONE_FORECAST_DEFAULT_ZONE_IMPRESSIONS. * * Note also: * - If the zone ID exists in the $this->aNewZoneIDs array, then all operation * intervals for the past week will be updated, not just those in $aRanges. * * @access private * @param integer $zoneId The ID of the zone which may require its ZIF value(s) * to be calculated. * @param array $aRanges An array of arrays, containing ranges of operation * intervals that the zone will need its ZIF values * updated for. * @param boolean $newZone The zone is considered to be "new"; store this * information along with the forecasts. * * @return void */ function _calculateZoneImpressionForecastValues($zoneId, $aRanges, $newZone) { // Check the parameters if (!is_integer($zoneId) || $zoneId < 0) { return; } if (!is_array($aRanges) || empty($aRanges)) { return; } // Update the ZIF for all ranges foreach ($aRanges as $aRange) { // Get the two dates representing operation interval start // dates for the two operation interval IDs at the lower and // upper bounds of the range $tmp = array_keys($aRange); $min = min($tmp); $max = max($tmp); $oRangeLowerDate = new Date(); $oRangeLowerDate->copy($aRange[$min]['start']); $oRangeUpperDate = new Date(); $oRangeUpperDate->copy($aRange[$max]['start']); // Get the average impressions delivered by the zone in previous // operation intervals, for the required operation interval range $aZoneImpressionAverages = $this->_getZoneImpressionAverages($zoneId, $oRangeLowerDate, $oRangeUpperDate); // Get the details of all forecast and actual impressions of the // zone for the required operation interval range, offset by the // required time interval, so that current trends in differences // between forecast and actual delivery can be calculated $oTrendLowerDate = $this->_getTrendLowerDate($oRangeLowerDate); $oTrendUpperDate = $this->_getTrendUpperDate($oRangeUpperDate); $aZoneForecastAndImpressionHistory = $this->oDal->getZonePastForecastAndImpressionHistory($zoneId, $oTrendLowerDate, $oTrendUpperDate); foreach ($aRange as $intervalId => $aInterval) { if (!isset($aZoneImpressionAverages[$intervalId])) { // This zone does not have a past average actual impressions delivered // value for this operation interval ID, and so cannot have been running // for longer than ZONE_FORECAST_BASELINE_WEEKS - as a result, either // forecast the value based on the past operation interval's data, or // use the default value $previousIntervalID = OX_OperationInterval::previousOperationIntervalID($intervalId); if (isset($aZoneForecastAndImpressionHistory[$previousIntervalID]['actual_impressions']) && $aZoneForecastAndImpressionHistory[$previousIntervalID]['actual_impressions'] > 0) { // Use the previous operation interval's actual impressions value as the // new forecast OA::debug(" - Forecasting for OI {$intervalId} (starting '" . $aInterval['start']->format('%Y-%m-%d %H:%M:%S') . ' ' . $aInterval['start']->tz->getShortName() . "') based on previous OI value", PEAR_LOG_DEBUG); $this->_storeForecast($this->aForecastResults, $aZoneForecastAndImpressionHistory, $zoneId, $intervalId, $aInterval, $aZoneForecastAndImpressionHistory[$previousIntervalID]['actual_impressions'], $newZone); } else { // Use the default value as the new forecast, and note that the forecast // is so based OA::debug(" - Forecasting for OI {$intervalId} (starting '" . $aInterval['start']->format('%Y-%m-%d %H:%M:%S') . ' ' . $aInterval['start']->tz->getShortName() . "') based on default value", PEAR_LOG_DEBUG); $this->_storeForecast($this->aForecastResults, $aZoneForecastAndImpressionHistory, $zoneId, $intervalId, $aInterval, $this->ZONE_FORECAST_DEFAULT_ZONE_IMPRESSIONS, $newZone, true); } } else { // Get the lower bound operation interval ID of the trend calculation // range required for this operation interval ID $offetOperationId = OX_OperationInterval::previousOperationIntervalID($intervalId, null, $this->_getTrendOperationIntervalStartOffset()); // Set the initial forecast and actual impressions values $forecastImpressions = 0; $actualImpressions = 0; // Loop over the trend adjustment range data appropriate to this operation // interval ID, and sum up the forecast and actual impression values for ($i = 0; $i < ZONE_FORECAST_TREND_OPERATION_INTERVALS; $i++) { if (!isset($aZoneForecastAndImpressionHistory[$offetOperationId])) { // The forecast/impression history of this zone is incomplete, so the // trend adjustment information cannot be calculated $forecastImpressions = false; $actualImpressions = false; break; } // Sum the actual impression value for the current trend adjustment interval if (!empty($aZoneForecastAndImpressionHistory[$offetOperationId]['actual_impressions'])) { $actualImpressions += $aZoneForecastAndImpressionHistory[$offetOperationId]['actual_impressions']; } // Sum the forecast impression value for the current trend adjustment interval if ($aZoneForecastAndImpressionHistory[$offetOperationId]['forecast_impressions'] < 1) { // Ack, that's a bad forecast impression value - don't trust the data! $forecastImpressions = false; $actualImpressions = false; break; } $forecastImpressions += $aZoneForecastAndImpressionHistory[$offetOperationId]['forecast_impressions']; // Go to the next operation ID in the trend range $offetOperationId = OX_OperationInterval::nextOperationIntervalID($offetOperationId); } unset($forecastAverage); if ($forecastImpressions !== false) { // Calculate the average forecast impression value for the trend adjustment range $forecastAverage = $forecastImpressions / ZONE_FORECAST_TREND_OPERATION_INTERVALS; } unset($actualAverage); if ($actualImpressions !== false) { // Calculate the average actual impression value for the trend adjustment range $actualAverage = round($actualImpressions / ZONE_FORECAST_TREND_OPERATION_INTERVALS); } if (isset($forecastAverage) && $forecastAverage > 0 && isset($actualAverage)) { // The past average forecast and actual impression values exist, so calculate the // trend adjustment value, and calculate the new forecast from the past average and // the trend adjustment value OA::debug(" - Forecasting for OI {$intervalId} (starting '" . $aInterval['start']->format('%Y-%m-%d %H:%M:%S') . ' ' . $aInterval['start']->tz->getShortName() . "') based on past average and recent trend", PEAR_LOG_DEBUG); $trendValue = $actualAverage / $forecastAverage; $this->_storeForecast($this->aForecastResults, $aZoneForecastAndImpressionHistory, $zoneId, $intervalId, $aInterval, $aZoneImpressionAverages[$intervalId] * $trendValue, $newZone); } else { // The trend data could not be calculated, so simply use the past average as the // new forecast OA::debug(" - Forecasting for OI {$intervalId} (starting '" . $aInterval['start']->format('%Y-%m-%d %H:%M:%S') . ' ' . $aInterval['start']->tz->getShortName() . "') based on past average only", PEAR_LOG_DEBUG); $this->_storeForecast($this->aForecastResults, $aZoneForecastAndImpressionHistory, $zoneId, $intervalId, $aInterval, $aZoneImpressionAverages[$intervalId], $newZone); } } } } }
/** * A method to test the nextOperationIntervalID() method. */ function testNextOperationIntervalID() { $operationIntervalID = 166; $operationInterval = 60; $operationIntervalID = OX_OperationInterval::nextOperationIntervalID($operationIntervalID, $operationInterval); $this->assertEqual($operationIntervalID, 167); $operationIntervalID = OX_OperationInterval::nextOperationIntervalID($operationIntervalID, $operationInterval); $this->assertEqual($operationIntervalID, 0); $operationIntervalID = OX_OperationInterval::nextOperationIntervalID($operationIntervalID, $operationInterval); $this->assertEqual($operationIntervalID, 1); $operationIntervalID = 166; $operationInterval = 60; $intervals = 3; $operationIntervalID = OX_OperationInterval::nextOperationIntervalID($operationIntervalID, $operationInterval, $intervals); $this->assertEqual($operationIntervalID, 1); $operationIntervalID = 334; $operationInterval = 30; $operationIntervalID = OX_OperationInterval::nextOperationIntervalID($operationIntervalID, $operationInterval); $this->assertEqual($operationIntervalID, 335); $operationIntervalID = OX_OperationInterval::nextOperationIntervalID($operationIntervalID, $operationInterval); $this->assertEqual($operationIntervalID, 0); $operationIntervalID = OX_OperationInterval::nextOperationIntervalID($operationIntervalID, $operationInterval); $this->assertEqual($operationIntervalID, 1); $operationIntervalID = 334; $operationInterval = 30; $intervals = 3; $operationIntervalID = OX_OperationInterval::nextOperationIntervalID($operationIntervalID, $operationInterval, $intervals); $this->assertEqual($operationIntervalID, 1); }
/** * A method to test the getAdLifetimeZoneImpressionsRemaining() method. * * Test 1: Test with invalid parameters, and ensure that zero is returned. * Test 2: Test with equal start and end dates, and ensure just that OI's * data is returned. * Test 3: Test with a small range of dates in one week, that the correct * sum is returned. * Test 4: Test with a small range of dates over three days, covering two * weeks, and ensure that the correct result is returned. * Test 5: Test with a limitation that blocks less than 50% of the remaining * range, and ensure that the correct result is returned. * Test 6: Test with a limitation that blocks more than 50% of the remaining * range, and ensure that the correct result is returned. */ function testGetAdLifetimeZoneImpressionsRemaining() { $aConf =& $GLOBALS['_MAX']['CONF']; $aConf['maintenance']['operationInterval'] = 60; $aDeliveryLimitations = array(); $oDeliveryLimitationManager = new OA_Maintenance_Priority_DeliveryLimitation($aDeliveryLimitations); // Test 1 $oDate = new Date('2006-02-15 11:07:15'); $aCumulativeZoneForecast = array(); $aCumulativeZoneForecast = $this->_fillForecastArray($aCumulativeZoneForecast); $result = $oDeliveryLimitationManager->getAdLifetimeZoneImpressionsRemaining('foo', $oDate, $aCumulativeZoneForecast); $this->assertEqual($result, 0); $result = $oDeliveryLimitationManager->getAdLifetimeZoneImpressionsRemaining($oDate, 'foo', $aCumulativeZoneForecast); $this->assertEqual($result, 0); $result = $oDeliveryLimitationManager->getAdLifetimeZoneImpressionsRemaining($oDate, $oDate, 'foo'); $this->assertEqual($result, 0); // Test 2 $oDate = new Date('2006-02-15 23:07:15'); $aCumulativeZoneForecast = array(); $aCumulativeZoneForecast = $this->_fillForecastArray($aCumulativeZoneForecast); $result = $oDeliveryLimitationManager->getAdLifetimeZoneImpressionsRemaining($oDate, $oDate, $aCumulativeZoneForecast); $this->assertEqual($result, 1); $aDates = OX_OperationInterval::convertDateToOperationIntervalStartAndEndDates($oDate); $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID($aDates['start']); $aCumulativeZoneForecast[$operationIntervalID] = 50; $previousOperationIntervalId = OX_OperationInterval::previousOperationIntervalID($operationIntervalID); $aCumulativeZoneForecast[$previousOperationIntervalId] = 5; $nextOperationIntervalId = OX_OperationInterval::nextOperationIntervalID($operationIntervalID); $aCumulativeZoneForecast[$nextOperationIntervalId] = 7; $aCumulativeZoneForecast = $this->_fillForecastArray($aCumulativeZoneForecast); $result = $oDeliveryLimitationManager->getAdLifetimeZoneImpressionsRemaining($oDate, $oDate, $aCumulativeZoneForecast); $this->assertEqual($result, 50); // Test 3 $oStartDate = new Date('2006-02-15 11:07:15'); $oEndDate = new Date('2006-02-15 23:59:59'); $aCumulativeZoneForecast = array(); $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-15 10:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 1; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-15 11:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 10; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-15 12:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 100; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-15 13:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 1000; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-15 14:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 10000; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-15 15:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 100000; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-15 16:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 1000000; $aCumulativeZoneForecast = $this->_fillForecastArray($aCumulativeZoneForecast); $result = $oDeliveryLimitationManager->getAdLifetimeZoneImpressionsRemaining($oStartDate, $oEndDate, $aCumulativeZoneForecast); $this->assertEqual($result, 1111110 + 7); // Test 4 $oStartDate = new Date('2006-02-18 22:07:15'); $oEndDate = new Date('2006-02-20 23:59:59'); $aCumulativeZoneForecast = array(); $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-18 21:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 1; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-18 22:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 10; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-18 23:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 100; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-19 00:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 1000; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-19 01:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 10000; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-19 02:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 100000; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-19 03:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 1000000; $operationIntervalID = OX_OperationInterval::convertDateToOperationIntervalID(new Date('2006-02-19 04:00:01')); $aCumulativeZoneForecast[$operationIntervalID] = 10000000; $aCumulativeZoneForecast = $this->_fillForecastArray($aCumulativeZoneForecast); $result = $oDeliveryLimitationManager->getAdLifetimeZoneImpressionsRemaining($oStartDate, $oEndDate, $aCumulativeZoneForecast); $this->assertEqual($result, 110 + 11111000 + 19 + 24); // Test 5 $oStartDate = new Date('2006-02-07 12:07:15'); $oEndDate = new Date('2006-02-07 23:59:59'); $aDeliveryLimitations = array(array('ad_id' => 1, 'logical' => 'and', 'type' => 'deliveryLimitations:Time:Hour', 'comparison' => '!~', 'data' => '23', 'executionorder' => 0)); $oDeliveryLimitationManager = new OA_Maintenance_Priority_DeliveryLimitation($aDeliveryLimitations); $oDeliveryLimitationManager->getActiveAdOperationIntervals(12, $oStartDate, $oEndDate); $aCumulativeZoneForecast = array(); $aCumulativeZoneForecast = $this->_fillForecastArray($aCumulativeZoneForecast); $result = $oDeliveryLimitationManager->getAdLifetimeZoneImpressionsRemaining($oStartDate, $oEndDate, $aCumulativeZoneForecast); $this->assertEqual($result, 11); // Test 6 $oStartDate = new Date('2006-02-07 12:07:15'); $oEndDate = new Date('2006-02-08 23:59:59'); $aDeliveryLimitations = array(array('ad_id' => 1, 'logical' => 'and', 'type' => 'deliveryLimitations:Time:Hour', 'comparison' => '=~', 'data' => '22', 'executionorder' => 0)); $oDeliveryLimitationManager = new OA_Maintenance_Priority_DeliveryLimitation($aDeliveryLimitations); $oDeliveryLimitationManager->getActiveAdOperationIntervals(12, $oStartDate, $oEndDate); $aCumulativeZoneForecast = array(); $aCumulativeZoneForecast = $this->_fillForecastArray($aCumulativeZoneForecast); $result = $oDeliveryLimitationManager->getAdLifetimeZoneImpressionsRemaining($oStartDate, $oEndDate, $aCumulativeZoneForecast); $this->assertEqual($result, 2); TestEnv::restoreConfig(); }