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); } }
/** * 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; }
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; }
/** * @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))); }
/** * 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); }
/** * @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); }
/** * 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; }
/** * @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()); }