예제 #1
0
 /**
  * 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();
 }