diff options
author | mattab <matthieu.aubry@gmail.com> | 2014-12-05 12:42:43 +0300 |
---|---|---|
committer | mattab <matthieu.aubry@gmail.com> | 2014-12-05 12:42:43 +0300 |
commit | 8ba4ba154aab7ac286b66ff16b59bb798ac3fb45 (patch) | |
tree | 1ed75cf396efc3965835bdfdb00a651ea8b8b7d2 /plugins | |
parent | 7f509b059ec54109cd2d7200dc466c4944c33d53 (diff) |
Refactor Live API Visitor Profile logic in own class
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/Live/API.php | 262 | ||||
-rw-r--r-- | plugins/Live/Controller.php | 2 | ||||
-rw-r--r-- | plugins/Live/Model.php | 20 | ||||
-rw-r--r-- | plugins/Live/VisitorProfile.php | 279 |
4 files changed, 295 insertions, 268 deletions
diff --git a/plugins/Live/API.php b/plugins/Live/API.php index 0b66e75f64..fc08e510db 100644 --- a/plugins/Live/API.php +++ b/plugins/Live/API.php @@ -16,10 +16,8 @@ use Piwik\DataTable\Row; use Piwik\Date; use Piwik\Db; use Piwik\Metrics\Formatter; -use Piwik\Period\Range; use Piwik\Period; use Piwik\Piwik; -use Piwik\Plugins\Referrers\API as APIReferrers; use Piwik\Plugins\SitesManager\API as APISitesManager; use Piwik\Segment; use Piwik\Site; @@ -55,8 +53,6 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/functions.php'; class API extends \Piwik\Plugin\API { const VISITOR_PROFILE_MAX_VISITS_TO_AGGREGATE = 100; - const VISITOR_PROFILE_MAX_VISITS_TO_SHOW = 10; - const VISITOR_PROFILE_DATE_FORMAT = '%day% %shortMonth% %longYear%'; /** * This will return simple counters, for a given website ID, for visits over the last N minutes @@ -166,190 +162,8 @@ class API extends \Piwik\Plugin\API return array(); } - $isEcommerceEnabled = Site::isEcommerceEnabledFor($idSite); - - $result = array(); - $result['totalVisits'] = 0; - $result['totalVisitDuration'] = 0; - $result['totalActions'] = 0; - $result['totalSearches'] = 0; - $result['totalPageViews'] = 0; - $result['totalGoalConversions'] = 0; - $result['totalConversionsByGoal'] = array(); - - if ($isEcommerceEnabled) { - $result['totalEcommerceConversions'] = 0; - $result['totalEcommerceRevenue'] = 0; - $result['totalEcommerceItems'] = 0; - $result['totalAbandonedCarts'] = 0; - $result['totalAbandonedCartsRevenue'] = 0; - $result['totalAbandonedCartsItems'] = 0; - } - - $countries = array(); - $continents = array(); - $cities = array(); - $siteSearchKeywords = array(); - - $pageGenerationTimeTotal = 0; - - // aggregate all requested visits info for total_* info - foreach ($visits->getRows() as $visit) { - ++$result['totalVisits']; - - $result['totalVisitDuration'] += $visit->getColumn('visitDuration'); - $result['totalActions'] += $visit->getColumn('actions'); - $result['totalGoalConversions'] += $visit->getColumn('goalConversions'); - - // individual goal conversions are stored in action details - foreach ($visit->getColumn('actionDetails') as $action) { - if ($action['type'] == 'goal') { - // handle goal conversion - $idGoal = $action['goalId']; - $idGoalKey = 'idgoal=' . $idGoal; - - if (!isset($result['totalConversionsByGoal'][$idGoalKey])) { - $result['totalConversionsByGoal'][$idGoalKey] = 0; - } - ++$result['totalConversionsByGoal'][$idGoalKey]; - - if (!empty($action['revenue'])) { - if (!isset($result['totalRevenueByGoal'][$idGoalKey])) { - $result['totalRevenueByGoal'][$idGoalKey] = 0; - } - $result['totalRevenueByGoal'][$idGoalKey] += $action['revenue']; - } - } else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER // handle ecommerce order - && $isEcommerceEnabled - ) { - ++$result['totalEcommerceConversions']; - $result['totalEcommerceRevenue'] += $action['revenue']; - $result['totalEcommerceItems'] += $action['items']; - } else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART // handler abandoned cart - && $isEcommerceEnabled - ) { - ++$result['totalAbandonedCarts']; - $result['totalAbandonedCartsRevenue'] += $action['revenue']; - $result['totalAbandonedCartsItems'] += $action['items']; - } - - if (isset($action['siteSearchKeyword'])) { - $keyword = $action['siteSearchKeyword']; - - if (!isset($siteSearchKeywords[$keyword])) { - $siteSearchKeywords[$keyword] = 0; - ++$result['totalSearches']; - } - ++$siteSearchKeywords[$keyword]; - } - - if (isset($action['generationTime'])) { - $pageGenerationTimeTotal += $action['generationTime']; - ++$result['totalPageViews']; - } - } - - $countryCode = $visit->getColumn('countryCode'); - if (!isset($countries[$countryCode])) { - $countries[$countryCode] = 0; - } - ++$countries[$countryCode]; - - $continentCode = $visit->getColumn('continentCode'); - if (!isset($continents[$continentCode])) { - $continents[$continentCode] = 0; - } - ++$continents[$continentCode]; - - if ($countryCode && !array_key_exists($countryCode, $cities)) { - $cities[$countryCode] = array(); - } - $city = $visit->getColumn('city'); - if (!empty($city)) { - $cities[$countryCode][] = $city; - } - } - - // sort countries/continents/search keywords by visit/action - asort($countries); - asort($continents); - arsort($siteSearchKeywords); - - // transform country/continents/search keywords into something that will look good in XML - $result['countries'] = $result['continents'] = $result['searches'] = array(); - - foreach ($countries as $countryCode => $nbVisits) { - - $countryInfo = array('country' => $countryCode, - 'nb_visits' => $nbVisits, - 'flag' => \Piwik\Plugins\UserCountry\getFlagFromCode($countryCode), - 'prettyName' => \Piwik\Plugins\UserCountry\countryTranslate($countryCode)); - if (!empty($cities[$countryCode])) { - $countryInfo['cities'] = array_unique($cities[$countryCode]); - } - $result['countries'][] = $countryInfo; - } - foreach ($continents as $continentCode => $nbVisits) { - $result['continents'][] = array('continent' => $continentCode, - 'nb_visits' => $nbVisits, - 'prettyName' => \Piwik\Plugins\UserCountry\continentTranslate($continentCode)); - } - foreach ($siteSearchKeywords as $keyword => $searchCount) { - $result['searches'][] = array('keyword' => $keyword, - 'searches' => $searchCount); - } - - if ($result['totalPageViews']) { - $result['averagePageGenerationTime'] = - round($pageGenerationTimeTotal / $result['totalPageViews'], $precision = 2); - } - - $formatter = new Formatter(); - $result['totalVisitDurationPretty'] = $formatter->getPrettyTimeFromSeconds($result['totalVisitDuration'], true); - - // use requested visits for first/last visit info - $rows = $visits->getRows(); - $result['firstVisit'] = $this->getVisitorProfileVisitSummary(end($rows)); - $result['lastVisit'] = $this->getVisitorProfileVisitSummary(reset($rows)); - - // check if requested visits have lat/long - if ($checkForLatLong) { - $result['hasLatLong'] = false; - foreach ($rows as $visit) { - if ($visit->getColumn('latitude') !== false) { // realtime map only checks for latitude - $result['hasLatLong'] = true; - break; - } - } - } - - // save count of visits we queries - $result['visitsAggregated'] = count($rows); - - // use N most recent visits for last_visits - $visits->deleteRowsOffset(self::VISITOR_PROFILE_MAX_VISITS_TO_SHOW); - $result['lastVisits'] = $visits; - - // use the right date format for the pretty server date - $timezone = Site::getTimezoneFor($idSite); - foreach ($result['lastVisits']->getRows() as $visit) { - $dateTimeVisitFirstAction = Date::factory($visit->getColumn('firstActionTimestamp'), $timezone); - - $datePretty = $dateTimeVisitFirstAction->getLocalized(self::VISITOR_PROFILE_DATE_FORMAT); - $visit->setColumn('serverDatePrettyFirstAction', $datePretty); - - $dateTimePretty = $datePretty . ' ' . $visit->getColumn('serverTimePrettyFirstAction'); - $visit->setColumn('serverDateTimePrettyFirstAction', $dateTimePretty); - } - - $result['userId'] = $visit->getColumn('userId'); - - // get visitor IDs that are adjacent to this one in log_visit - // TODO: make sure order of visitor ids is not changed if a returning visitor visits while the user is - // looking at the popup. - $latestVisitTime = reset($rows)->getColumn('lastActionDateTime'); - $result['nextVisitorId'] = $this->getAdjacentVisitorId($idSite, $visitorId, $latestVisitTime, $segment, $getNext = true); - $result['previousVisitorId'] = $this->getAdjacentVisitorId($idSite, $visitorId, $latestVisitTime, $segment, $getNext = false); + $profile = new VisitorProfile(); + $result = $profile->makeVisitorProfile($visits, $idSite, $visitorId, $segment, $checkForLatLong); /** * Triggered in the Live.getVisitorProfile API method. Plugins can use this event @@ -398,77 +212,6 @@ class API extends \Piwik\Plugin\API return $visitor->getVisitorId(); } - /** - * Returns the ID of a visitor that is adjacent to another visitor (by time of last action) - * in the log_visit table. - * - * @param int $idSite The ID of the site whose visits should be looked at. - * @param string $visitorId The ID of the visitor to get an adjacent visitor for. - * @param string $visitLastActionTime The last action time of the latest visit for $visitorId. - * @param string $segment - * @param bool $getNext Whether to retrieve the next visitor or the previous visitor. The next - * visitor will be the visitor that appears chronologically later in the - * log_visit table. The previous visitor will be the visitor that appears - * earlier. - * @return string The hex visitor ID. - */ - private function getAdjacentVisitorId($idSite, $visitorId, $visitLastActionTime, $segment, $getNext) - { - $model = new Model(); - return $model->queryAdjacentVisitorId($idSite, $visitorId, $visitLastActionTime, $segment, $getNext); - } - - /** - * Returns a summary for an important visit. Used to describe the first & last visits of a visitor. - * - * @param Row $visit - * @return array - */ - private function getVisitorProfileVisitSummary($visit) - { - $today = Date::today(); - - $serverDate = $visit->getColumn('firstActionTimestamp'); - return array( - 'date' => $serverDate, - 'prettyDate' => Date::factory($serverDate)->getLocalized(self::VISITOR_PROFILE_DATE_FORMAT), - 'daysAgo' => (int)Date::secondsToDays($today->getTimestamp() - Date::factory($serverDate)->getTimestamp()), - 'referrerType' => $visit->getColumn('referrerType'), - 'referralSummary' => self::getReferrerSummaryForVisit($visit), - ); - } - - /** - * Returns a summary for a visit's referral. - * - * @param Row $visit - * @return bool|mixed|string - * @ignore - */ - public static function getReferrerSummaryForVisit($visit) - { - $referrerType = $visit->getColumn('referrerType'); - if ($referrerType === false - || $referrerType == 'direct' - ) { - $result = Piwik::translate('Referrers_DirectEntry'); - } else if ($referrerType == 'search') { - $result = $visit->getColumn('referrerName'); - - $keyword = $visit->getColumn('referrerKeyword'); - if ($keyword !== false - && $keyword != APIReferrers::getKeywordNotDefinedString() - ) { - $result .= ' (' . $keyword . ')'; - } - } else if ($referrerType == 'campaign') { - $result = Piwik::translate('Referrers_ColumnCampaign') . ' (' . $visit->getColumn('referrerName') . ')'; - } else { - $result = $visit->getColumn('referrerName'); - } - - return $result; - } /** * @deprecated @@ -545,7 +288,6 @@ class API extends \Piwik\Plugin\API { $model = new Model(); $data = $model->queryLogVisits($idSite, $period, $date, $segment, $countVisitorsToFetch, $visitorId, $minTimestamp, $filterSortOrder); - return $this->makeVisitorTableFromArray($data); } diff --git a/plugins/Live/Controller.php b/plugins/Live/Controller.php index 9df3e26ff8..da434412dd 100644 --- a/plugins/Live/Controller.php +++ b/plugins/Live/Controller.php @@ -133,7 +133,7 @@ class Controller extends \Piwik\Plugin\Controller 'date' => false )); $view->visitData = $visits->getFirstRow()->getColumns(); - $view->visitReferralSummary = API::getReferrerSummaryForVisit($visits->getFirstRow()); + $view->visitReferralSummary = VisitorProfile::getReferrerSummaryForVisit($visits->getFirstRow()); $view->showLocation = true; $this->setWidgetizedVisitorProfileUrl($view); $view->exportLink = $this->getVisitorProfileExportLink(); diff --git a/plugins/Live/Model.php b/plugins/Live/Model.php index 7bf5485c5a..2353450572 100644 --- a/plugins/Live/Model.php +++ b/plugins/Live/Model.php @@ -7,7 +7,7 @@ * */ -namespace Piwik\plugins\Live; +namespace Piwik\Plugins\Live; use Exception; use Piwik\Common; @@ -216,12 +216,18 @@ class Model /** - * @param $idSite - * @param $visitorId - * @param $visitLastActionTime - * @param $segment - * @param $getNext - * @return string + * Returns the ID of a visitor that is adjacent to another visitor (by time of last action) + * in the log_visit table. + * + * @param int $idSite The ID of the site whose visits should be looked at. + * @param string $visitorId The ID of the visitor to get an adjacent visitor for. + * @param string $visitLastActionTime The last action time of the latest visit for $visitorId. + * @param string $segment + * @param bool $getNext Whether to retrieve the next visitor or the previous visitor. The next + * visitor will be the visitor that appears chronologically later in the + * log_visit table. The previous visitor will be the visitor that appears + * earlier. + * @return string The hex visitor ID. * @throws Exception */ public function queryAdjacentVisitorId($idSite, $visitorId, $visitLastActionTime, $segment, $getNext) diff --git a/plugins/Live/VisitorProfile.php b/plugins/Live/VisitorProfile.php new file mode 100644 index 0000000000..807028d6ad --- /dev/null +++ b/plugins/Live/VisitorProfile.php @@ -0,0 +1,279 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Plugins\Live; + +use Exception; +use Piwik\DataTable; +use Piwik\Date; +use Piwik\Metrics\Formatter; +use Piwik\Piwik; +use Piwik\Site; +use Piwik\Plugins\Referrers\API as APIReferrers; + +class VisitorProfile +{ + const VISITOR_PROFILE_MAX_VISITS_TO_SHOW = 10; + const VISITOR_PROFILE_DATE_FORMAT = '%day% %shortMonth% %longYear%'; + + /** + * @param $visits + * @param $idSite + * @param $visitorId + * @param $segment + * @param $checkForLatLong + * @return array + * @throws Exception + */ + public function makeVisitorProfile(DataTable $visits, $idSite, $visitorId, $segment, $checkForLatLong) + { + $isEcommerceEnabled = Site::isEcommerceEnabledFor($idSite); + + $result = array(); + $result['totalVisits'] = 0; + $result['totalVisitDuration'] = 0; + $result['totalActions'] = 0; + $result['totalSearches'] = 0; + $result['totalPageViews'] = 0; + $result['totalGoalConversions'] = 0; + $result['totalConversionsByGoal'] = array(); + + if ($isEcommerceEnabled) { + $result['totalEcommerceConversions'] = 0; + $result['totalEcommerceRevenue'] = 0; + $result['totalEcommerceItems'] = 0; + $result['totalAbandonedCarts'] = 0; + $result['totalAbandonedCartsRevenue'] = 0; + $result['totalAbandonedCartsItems'] = 0; + } + + $countries = array(); + $continents = array(); + $cities = array(); + $siteSearchKeywords = array(); + + $pageGenerationTimeTotal = 0; + + // aggregate all requested visits info for total_* info + /** @var DataTable\Row $visit */ + foreach ($visits->getRows() as $visit) { + ++$result['totalVisits']; + + $result['totalVisitDuration'] += $visit->getColumn('visitDuration'); + $result['totalActions'] += $visit->getColumn('actions'); + $result['totalGoalConversions'] += $visit->getColumn('goalConversions'); + + // individual goal conversions are stored in action details + foreach ($visit->getColumn('actionDetails') as $action) { + if ($action['type'] == 'goal') { + // handle goal conversion + $idGoal = $action['goalId']; + $idGoalKey = 'idgoal=' . $idGoal; + + if (!isset($result['totalConversionsByGoal'][$idGoalKey])) { + $result['totalConversionsByGoal'][$idGoalKey] = 0; + } + ++$result['totalConversionsByGoal'][$idGoalKey]; + + if (!empty($action['revenue'])) { + if (!isset($result['totalRevenueByGoal'][$idGoalKey])) { + $result['totalRevenueByGoal'][$idGoalKey] = 0; + } + $result['totalRevenueByGoal'][$idGoalKey] += $action['revenue']; + } + } else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER // handle ecommerce order + && $isEcommerceEnabled + ) { + ++$result['totalEcommerceConversions']; + $result['totalEcommerceRevenue'] += $action['revenue']; + $result['totalEcommerceItems'] += $action['items']; + } else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART // handler abandoned cart + && $isEcommerceEnabled + ) { + ++$result['totalAbandonedCarts']; + $result['totalAbandonedCartsRevenue'] += $action['revenue']; + $result['totalAbandonedCartsItems'] += $action['items']; + } + + if (isset($action['siteSearchKeyword'])) { + $keyword = $action['siteSearchKeyword']; + + if (!isset($siteSearchKeywords[$keyword])) { + $siteSearchKeywords[$keyword] = 0; + ++$result['totalSearches']; + } + ++$siteSearchKeywords[$keyword]; + } + + if (isset($action['generationTime'])) { + $pageGenerationTimeTotal += $action['generationTime']; + ++$result['totalPageViews']; + } + } + + $countryCode = $visit->getColumn('countryCode'); + if (!isset($countries[$countryCode])) { + $countries[$countryCode] = 0; + } + ++$countries[$countryCode]; + + $continentCode = $visit->getColumn('continentCode'); + if (!isset($continents[$continentCode])) { + $continents[$continentCode] = 0; + } + ++$continents[$continentCode]; + + if ($countryCode && !array_key_exists($countryCode, $cities)) { + $cities[$countryCode] = array(); + } + $city = $visit->getColumn('city'); + if (!empty($city)) { + $cities[$countryCode][] = $city; + } + } + + // sort countries/continents/search keywords by visit/action + asort($countries); + asort($continents); + arsort($siteSearchKeywords); + + // transform country/continents/search keywords into something that will look good in XML + $result['countries'] = $result['continents'] = $result['searches'] = array(); + + foreach ($countries as $countryCode => $nbVisits) { + + $countryInfo = array('country' => $countryCode, + 'nb_visits' => $nbVisits, + 'flag' => \Piwik\Plugins\UserCountry\getFlagFromCode($countryCode), + 'prettyName' => \Piwik\Plugins\UserCountry\countryTranslate($countryCode)); + if (!empty($cities[$countryCode])) { + $countryInfo['cities'] = array_unique($cities[$countryCode]); + } + $result['countries'][] = $countryInfo; + } + foreach ($continents as $continentCode => $nbVisits) { + $result['continents'][] = array('continent' => $continentCode, + 'nb_visits' => $nbVisits, + 'prettyName' => \Piwik\Plugins\UserCountry\continentTranslate($continentCode)); + } + foreach ($siteSearchKeywords as $keyword => $searchCount) { + $result['searches'][] = array('keyword' => $keyword, + 'searches' => $searchCount); + } + + if ($result['totalPageViews']) { + $result['averagePageGenerationTime'] = + round($pageGenerationTimeTotal / $result['totalPageViews'], $precision = 2); + } + + $formatter = new Formatter(); + $result['totalVisitDurationPretty'] = $formatter->getPrettyTimeFromSeconds($result['totalVisitDuration'], true); + + // use requested visits for first/last visit info + $rows = $visits->getRows(); + $result['firstVisit'] = $this->getVisitorProfileVisitSummary(end($rows)); + $result['lastVisit'] = $this->getVisitorProfileVisitSummary(reset($rows)); + + // check if requested visits have lat/long + if ($checkForLatLong) { + $result['hasLatLong'] = false; + foreach ($rows as $visit) { + if ($visit->getColumn('latitude') !== false) { // realtime map only checks for latitude + $result['hasLatLong'] = true; + break; + } + } + } + + // save count of visits we queries + $result['visitsAggregated'] = count($rows); + + // use N most recent visits for last_visits + $visits->deleteRowsOffset(self::VISITOR_PROFILE_MAX_VISITS_TO_SHOW); + $result['lastVisits'] = $visits; + + // use the right date format for the pretty server date + $timezone = Site::getTimezoneFor($idSite); + foreach ($result['lastVisits']->getRows() as $visit) { + $dateTimeVisitFirstAction = Date::factory($visit->getColumn('firstActionTimestamp'), $timezone); + + $datePretty = $dateTimeVisitFirstAction->getLocalized(self::VISITOR_PROFILE_DATE_FORMAT); + $visit->setColumn('serverDatePrettyFirstAction', $datePretty); + + $dateTimePretty = $datePretty . ' ' . $visit->getColumn('serverTimePrettyFirstAction'); + $visit->setColumn('serverDateTimePrettyFirstAction', $dateTimePretty); + } + + $result['userId'] = $visit->getColumn('userId'); + + // get visitor IDs that are adjacent to this one in log_visit + // TODO: make sure order of visitor ids is not changed if a returning visitor visits while the user is + // looking at the popup. + $latestVisitTime = reset($rows)->getColumn('lastActionDateTime'); + + + $model = new Model(); + $result['nextVisitorId'] = $model->queryAdjacentVisitorId($idSite, $visitorId, $latestVisitTime, $segment, $getNext = true); + $result['previousVisitorId'] = $model->queryAdjacentVisitorId($idSite, $visitorId, $latestVisitTime, $segment, $getNext = false); + return $result; + } + + /** + * Returns a summary for an important visit. Used to describe the first & last visits of a visitor. + * + * @param DataTable\Row $visit + * @return array + */ + private function getVisitorProfileVisitSummary($visit) + { + $today = Date::today(); + + $serverDate = $visit->getColumn('firstActionTimestamp'); + return array( + 'date' => $serverDate, + 'prettyDate' => Date::factory($serverDate)->getLocalized(self::VISITOR_PROFILE_DATE_FORMAT), + 'daysAgo' => (int)Date::secondsToDays($today->getTimestamp() - Date::factory($serverDate)->getTimestamp()), + 'referrerType' => $visit->getColumn('referrerType'), + 'referralSummary' => self::getReferrerSummaryForVisit($visit), + ); + } + + + /** + * Returns a summary for a visit's referral. + * + * @param DataTable\Row $visit + * @return bool|mixed|string + */ + public static function getReferrerSummaryForVisit($visit) + { + $referrerType = $visit->getColumn('referrerType'); + if ($referrerType === false + || $referrerType == 'direct' + ) { + $result = Piwik::translate('Referrers_DirectEntry'); + } else if ($referrerType == 'search') { + $result = $visit->getColumn('referrerName'); + + $keyword = $visit->getColumn('referrerKeyword'); + if ($keyword !== false + && $keyword != APIReferrers::getKeywordNotDefinedString() + ) { + $result .= ' (' . $keyword . ')'; + } + } else if ($referrerType == 'campaign') { + $result = Piwik::translate('Referrers_ColumnCampaign') . ' (' . $visit->getColumn('referrerName') . ')'; + } else { + $result = $visit->getColumn('referrerName'); + } + + return $result; + } + +}
\ No newline at end of file |