public function afterRequestProcessed(VisitProperties $visitProperties, Request $request)
 {
     $goalsConverted = $request->getMetadata('Goals', 'goalsConverted');
     if (!empty($goalsConverted)) {
         $isThereExistingCartInVisit = $this->goalManager->detectIsThereExistingCartInVisit($visitProperties->getProperties());
         $request->setMetadata('Goals', 'isThereExistingCartInVisit', $isThereExistingCartInVisit);
     }
 }
 public function recordLogs(VisitProperties $visitProperties, Request $request)
 {
     // record the goals if there were conversions in this request (even if the visit itself was not converted)
     $goalsConverted = $request->getMetadata('Goals', 'goalsConverted');
     if (!empty($goalsConverted)) {
         $this->goalManager->recordGoals($visitProperties, $request);
     }
 }
Пример #3
0
 /**
  * Gather fields=>values that needs to be updated for the existing visit in log_visit
  *
  * @param $action
  * @param $visitIsConverted
  * @return array
  */
 protected function getExistingVisitFieldsToUpdate($action, $visitIsConverted)
 {
     $valuesToUpdate = array();
     if ($action) {
         $idActionUrl = $action->getIdActionUrlForEntryAndExitIds();
         $idActionName = $action->getIdActionNameForEntryAndExitIds();
         $actionType = $action->getActionType();
         if ($idActionName !== false) {
             $valuesToUpdate['visit_exit_idaction_name'] = $idActionName;
         }
         $incrementActions = false;
         if ($idActionUrl !== false) {
             $valuesToUpdate['visit_exit_idaction_url'] = $idActionUrl;
             $incrementActions = true;
         }
         if ($actionType == Action::TYPE_SITE_SEARCH) {
             $valuesToUpdate['visit_total_searches'] = 'visit_total_searches + 1';
             $incrementActions = true;
         } else {
             if ($actionType == Action::TYPE_EVENT) {
                 $valuesToUpdate['visit_total_events'] = 'visit_total_events + 1';
                 $incrementActions = true;
             }
         }
         if ($incrementActions) {
             $valuesToUpdate['visit_total_actions'] = 'visit_total_actions + 1';
         }
     }
     $datetimeServer = Tracker::getDatetimeFromTimestamp($this->request->getCurrentTimestamp());
     $valuesToUpdate['visit_last_action_time'] = $datetimeServer;
     // Add 1 so it's always > 0
     $visitTotalTime = 1 + $this->request->getCurrentTimestamp() - $this->visitorInfo['visit_first_action_time'];
     $valuesToUpdate['visit_total_time'] = self::cleanupVisitTotalTime($visitTotalTime);
     // Goal conversion
     if ($visitIsConverted) {
         $valuesToUpdate['visit_goal_converted'] = 1;
         // If a pageview and goal conversion in the same second, with previously a goal conversion recorded
         // the request would not "update" the row since all values are the same as previous
         // therefore the request below throws exception, instead we make sure the UPDATE will affect the row
         $valuesToUpdate['visit_total_time'] = self::cleanupVisitTotalTime($valuesToUpdate['visit_total_time'] + $this->goalManager->idGoal + 2);
     }
     // Might update the idvisitor when it was forced or overwritten for this visit
     if (strlen($this->visitorInfo['idvisitor']) == Tracker::LENGTH_BINARY_ID) {
         $valuesToUpdate['idvisitor'] = $this->visitorInfo['idvisitor'];
     }
     // Ecommerce buyer status
     $visitEcommerceStatus = $this->goalManager->getBuyerType($this->visitorInfo['visit_goal_buyer']);
     if ($visitEcommerceStatus != GoalManager::TYPE_BUYER_NONE && $visitEcommerceStatus != $this->visitorInfo['visit_goal_buyer']) {
         $valuesToUpdate['visit_goal_buyer'] = $visitEcommerceStatus;
     }
     // Custom Variables overwrite previous values on each page view
     $valuesToUpdate = array_merge($valuesToUpdate, $this->visitorCustomVariables);
     return $valuesToUpdate;
 }
Пример #4
0
 private function getBuyerType(Request $request, $existingType = self::TYPE_BUYER_NONE)
 {
     $goalManager = new GoalManager($request);
     if (!$goalManager->requestIsEcommerce) {
         return $existingType;
     }
     if ($goalManager->isGoalAnOrder()) {
         return self::TYPE_BUYER_ORDERED;
     }
     // request is Add to Cart
     if ($existingType == self::TYPE_BUYER_ORDERED || $existingType == self::TYPE_BUYER_ORDERED_AND_OPEN_CART) {
         return self::TYPE_BUYER_ORDERED_AND_OPEN_CART;
     }
     return self::TYPE_BUYER_OPEN_CART;
 }
Пример #5
0
 /**
  * @internal param $this->getProcessor()
  */
 public function aggregateMultipleReports()
 {
     /*
      * Archive Ecommerce Items
      */
     $dataTableToSum = $this->dimensionRecord;
     foreach ($this->dimensionRecord as $recordName) {
         $dataTableToSum[] = self::getItemRecordNameAbandonedCart($recordName);
     }
     $this->getProcessor()->aggregateDataTableRecords($dataTableToSum);
     /*
      *  Archive General Goal metrics
      */
     $goalIdsToSum = GoalManager::getGoalIds($this->getProcessor()->getParams()->getSite()->getId());
     //Ecommerce
     $goalIdsToSum[] = GoalManager::IDGOAL_ORDER;
     $goalIdsToSum[] = GoalManager::IDGOAL_CART;
     //bug here if idgoal=1
     // Overall goal metrics
     $goalIdsToSum[] = false;
     $fieldsToSum = array();
     foreach ($goalIdsToSum as $goalId) {
         $metricsToSum = Goals::getGoalColumns($goalId);
         unset($metricsToSum[array_search('conversion_rate', $metricsToSum)]);
         foreach ($metricsToSum as $metricName) {
             $fieldsToSum[] = self::getRecordName($metricName, $goalId);
         }
     }
     $records = $this->getProcessor()->aggregateNumericMetrics($fieldsToSum);
     // also recording conversion_rate for each goal
     foreach ($goalIdsToSum as $goalId) {
         $nb_conversions = $records[self::getRecordName('nb_visits_converted', $goalId)];
         $conversion_rate = $this->getConversionRate($nb_conversions);
         $this->getProcessor()->insertNumericRecord(self::getRecordName('conversion_rate', $goalId), $conversion_rate);
         // sum up the visits to conversion data table & the days to conversion data table
         $this->getProcessor()->aggregateDataTableRecords(array(self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $goalId), self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $goalId)));
     }
     // sum up goal overview reports
     $this->getProcessor()->aggregateDataTableRecords(array(self::getRecordName(self::VISITS_UNTIL_RECORD_NAME), self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME)));
 }
Пример #6
0
 /**
  *    Main algorithm to handle the visit.
  *
  *  Once we have the visitor information, we have to determine if the visit is a new or a known visit.
  *
  * 1) When the last action was done more than 30min ago,
  *      or if the visitor is new, then this is a new visit.
  *
  * 2) If the last action is less than 30min ago, then the same visit is going on.
  *    Because the visit goes on, we can get the time spent during the last action.
  *
  * NB:
  *  - In the case of a new visit, then the time spent
  *    during the last action of the previous visit is unknown.
  *
  *    - In the case of a new visit but with a known visitor,
  *    we can set the 'returning visitor' flag.
  *
  * In all the cases we set a cookie to the visitor with the new information.
  */
 public function handle()
 {
     // the IP is needed by isExcluded() and GoalManager->recordGoals()
     $this->visitorInfo['location_ip'] = $this->request->getIp();
     $excluded = new VisitExcluded($this->request, $this->visitorInfo['location_ip']);
     if ($excluded->isExcluded()) {
         return;
     }
     /**
      * Triggered after visits are tested for exclusion so plugins can modify the IP address
      * persisted with a visit.
      *
      * This event is primarily used by the **PrivacyManager** plugin to anonymize IP addresses.
      *
      * @param string &$ip The visitor's IP address.
      */
     Piwik::postEvent('Tracker.setVisitorIp', array(&$this->visitorInfo['location_ip']));
     $this->visitorCustomVariables = $this->request->getCustomVariables($scope = 'visit');
     if (!empty($this->visitorCustomVariables)) {
         Common::printDebug("Visit level Custom Variables: ");
         Common::printDebug($this->visitorCustomVariables);
     }
     $this->goalManager = new GoalManager($this->request);
     $visitIsConverted = false;
     $action = null;
     $isManualGoalConversion = $this->goalManager->isManualGoalConversion();
     $requestIsEcommerce = $this->goalManager->requestIsEcommerce;
     if ($requestIsEcommerce) {
         $someGoalsConverted = true;
         // Mark the visit as Converted only if it is an order (not for a Cart update)
         if ($this->goalManager->isGoalAnOrder()) {
             $visitIsConverted = true;
         }
     } elseif ($isManualGoalConversion) {
         // this request is from the JS call to piwikTracker.trackGoal()
         $someGoalsConverted = $this->goalManager->detectGoalId($this->request->getIdSite());
         $visitIsConverted = $someGoalsConverted;
         // if we find a idgoal in the URL, but then the goal is not valid, this is most likely a fake request
         if (!$someGoalsConverted) {
             Common::printDebug('Invalid goal tracking request for goal id = ' . $this->goalManager->idGoal);
             return;
         }
     } else {
         // normal page view, potentially triggering a URL matching goal
         $action = Action::factory($this->request);
         $action->writeDebugInfo();
         $someGoalsConverted = $this->goalManager->detectGoalsMatchingUrl($this->request->getIdSite(), $action);
         $visitIsConverted = $someGoalsConverted;
         $action->loadIdsFromLogActionTable();
     }
     /***
      * Visitor recognition
      */
     $visitorId = $this->getSettingsObject()->getConfigId();
     $visitor = new Visitor($this->request, $visitorId, $this->visitorInfo, $this->visitorCustomVariables);
     $visitor->recognize();
     $this->visitorInfo = $visitor->getVisitorInfo();
     $isLastActionInTheSameVisit = $this->isLastActionInTheSameVisit($visitor);
     if (!$isLastActionInTheSameVisit) {
         Common::printDebug("Visitor detected, but last action was more than 30 minutes ago...");
     }
     // Known visit when:
     // ( - the visitor has the Piwik cookie with the idcookie ID used by Piwik to match the visitor
     //   OR
     //   - the visitor doesn't have the Piwik cookie but could be match using heuristics @see recognizeTheVisitor()
     // )
     // AND
     // - the last page view for this visitor was less than 30 minutes ago @see isLastActionInTheSameVisit()
     if ($visitor->isVisitorKnown() && $isLastActionInTheSameVisit) {
         $idReferrerActionUrl = $this->visitorInfo['visit_exit_idaction_url'];
         $idReferrerActionName = $this->visitorInfo['visit_exit_idaction_name'];
         try {
             $this->goalManager->detectIsThereExistingCartInVisit($this->visitorInfo);
             $this->handleExistingVisit($visitor, $action, $visitIsConverted);
             if (!is_null($action)) {
                 $action->record($visitor, $idReferrerActionUrl, $idReferrerActionName);
             }
         } catch (VisitorNotFoundInDb $e) {
             // There is an edge case when:
             // - two manual goal conversions happen in the same second
             // - which result in handleExistingVisit throwing the exception
             //   because the UPDATE didn't affect any rows (one row was found, but not updated since no field changed)
             // - the exception is caught here and will result in a new visit incorrectly
             // In this case, we cancel the current conversion to be recorded:
             if ($isManualGoalConversion || $requestIsEcommerce) {
                 $someGoalsConverted = $visitIsConverted = false;
             } else {
                 $visitor->setIsVisitorKnown(false);
             }
         }
     }
     // New visit when:
     // - the visitor has the Piwik cookie but the last action was performed more than 30 min ago @see isLastActionInTheSameVisit()
     // - the visitor doesn't have the Piwik cookie, and couldn't be matched in @see recognizeTheVisitor()
     // - the visitor does have the Piwik cookie but the idcookie and idvisit found in the cookie didn't match to any existing visit in the DB
     if (!$visitor->isVisitorKnown() || !$isLastActionInTheSameVisit) {
         $this->handleNewVisit($visitor, $action, $visitIsConverted);
         if (!is_null($action)) {
             $action->record($visitor, 0, 0);
         }
     }
     // update the cookie with the new visit information
     $this->request->setThirdPartyCookie($this->visitorInfo['idvisitor']);
     // record the goals if applicable
     if ($someGoalsConverted) {
         $this->goalManager->recordGoals($visitor, $this->visitorInfo, $this->visitorCustomVariables, $action);
     }
     unset($this->goalManager);
     unset($action);
 }
Пример #7
0
 /**
  * @param Request $request
  * @param Visitor $visitor
  * @param Action|null $action
  * @param GoalManager $goalManager
  *
  * @return mixed|false
  */
 public function onGoalConversion(Request $request, Visitor $visitor, $action, GoalManager $goalManager)
 {
     $defaultRevenue = $goalManager->getGoalColumn('revenue');
     $revenue = $request->getGoalRevenue($defaultRevenue);
     return $this->roundRevenueIfNeeded($revenue);
 }
Пример #8
0
 /**
  * This event is triggered when an any custom goal is converted. In this example we would store a the id of the
  * goal in the 'example_conversion_dimension' column if the visitor is known and nothing otherwise.
  * Return boolean false if you do not want to change the value in some cases. If you do not want to perform any
  * action on an ecommerce order at all it is recommended to just remove this method.
  *
  * @param Request $request
  * @param Visitor $visitor
  * @param Action|null $action
  * @param GoalManager $goalManager
  *
  * @return mixed|false
  */
 public function onGoalConversion(Request $request, Visitor $visitor, $action, GoalManager $goalManager)
 {
     $goalId = $goalManager->getGoalColumn('idgoal');
     if ($visitor->isVisitorKnown()) {
         return $goalId;
     }
     return false;
 }
Пример #9
0
 /**
  * @internal param $this->getProcessor()
  */
 public function aggregateMultipleReports()
 {
     /*
      * Archive Ecommerce Items
      */
     $dataTableToSum = $this->dimensionRecord;
     foreach ($this->dimensionRecord as $recordName) {
         $dataTableToSum[] = self::getItemRecordNameAbandonedCart($recordName);
     }
     $columnsAggregationOperation = null;
     $this->getProcessor()->aggregateDataTableRecords($dataTableToSum, $maximumRowsInDataTableLevelZero = null, $maximumRowsInSubDataTable = null, $columnToSortByBeforeTruncation = null, $columnsAggregationOperation, $columnsToRenameAfterAggregation = null, $countRowsRecursive = array());
     /*
      *  Archive General Goal metrics
      */
     $goalIdsToSum = GoalManager::getGoalIds($this->getProcessor()->getParams()->getSite()->getId());
     //Ecommerce
     $goalIdsToSum[] = GoalManager::IDGOAL_ORDER;
     $goalIdsToSum[] = GoalManager::IDGOAL_CART;
     //bug here if idgoal=1
     // Overall goal metrics
     $goalIdsToSum[] = false;
     $fieldsToSum = array();
     foreach ($goalIdsToSum as $goalId) {
         $metricsToSum = Goals::getGoalColumns($goalId);
         foreach ($metricsToSum as $metricName) {
             $fieldsToSum[] = self::getRecordName($metricName, $goalId);
         }
     }
     $this->getProcessor()->aggregateNumericMetrics($fieldsToSum);
     $columnsAggregationOperation = null;
     foreach ($goalIdsToSum as $goalId) {
         // sum up the visits to conversion data table & the days to conversion data table
         $this->getProcessor()->aggregateDataTableRecords(array(self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $goalId), self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $goalId)), $maximumRowsInDataTableLevelZero = null, $maximumRowsInSubDataTable = null, $columnToSortByBeforeTruncation = null, $columnsAggregationOperation, $columnsToRenameAfterAggregation = null, $countRowsRecursive = array());
     }
     $columnsAggregationOperation = null;
     // sum up goal overview reports
     $this->getProcessor()->aggregateDataTableRecords(array(self::getRecordName(self::VISITS_UNTIL_RECORD_NAME), self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME)), $maximumRowsInDataTableLevelZero = null, $maximumRowsInSubDataTable = null, $columnToSortByBeforeTruncation = null, $columnsAggregationOperation, $columnsToRenameAfterAggregation = null, $countRowsRecursive = array());
 }