diff options
author | mattab <matthieu.aubry@gmail.com> | 2014-12-05 13:33:37 +0300 |
---|---|---|
committer | mattab <matthieu.aubry@gmail.com> | 2014-12-05 13:33:37 +0300 |
commit | 13bb9a7ecdd65ea61caf70a449af13edf9cb2913 (patch) | |
tree | 756a7dbe10dbf0ed26d4b48dc24ff231bafde452 /plugins/Live | |
parent | af73717d45280335b33707295fc4a72f3937d74e (diff) |
Visitor Profile clean code
Diffstat (limited to 'plugins/Live')
-rw-r--r-- | plugins/Live/API.php | 4 | ||||
-rw-r--r-- | plugins/Live/VisitorProfile.php | 444 | ||||
-rw-r--r-- | plugins/Live/templates/getVisitorProfilePopup.twig | 2 |
3 files changed, 266 insertions, 184 deletions
diff --git a/plugins/Live/API.php b/plugins/Live/API.php index 970056001b..ed11c6c277 100644 --- a/plugins/Live/API.php +++ b/plugins/Live/API.php @@ -160,8 +160,8 @@ class API extends \Piwik\Plugin\API return array(); } - $profile = new VisitorProfile(); - $result = $profile->makeVisitorProfile($visits, $idSite, $visitorId, $segment); + $profile = new VisitorProfile($idSite); + $result = $profile->makeVisitorProfile($visits, $visitorId, $segment); /** * Triggered in the Live.getVisitorProfile API method. Plugins can use this event diff --git a/plugins/Live/VisitorProfile.php b/plugins/Live/VisitorProfile.php index 807028d6ad..d52adbf076 100644 --- a/plugins/Live/VisitorProfile.php +++ b/plugins/Live/VisitorProfile.php @@ -22,206 +22,69 @@ class VisitorProfile const VISITOR_PROFILE_MAX_VISITS_TO_SHOW = 10; const VISITOR_PROFILE_DATE_FORMAT = '%day% %shortMonth% %longYear%'; + protected $profile = array(); + private $siteSearchKeywords = array(); + private $continents = array(); + private $countries = array(); + private $cities = array(); + private $pageGenerationTimeTotal = 0; + + public function __construct($idSite) + { + $this->idSite = $idSite; + $this->isEcommerceEnabled = Site::isEcommerceEnabledFor($this->idSite); + } + /** * @param $visits - * @param $idSite * @param $visitorId * @param $segment - * @param $checkForLatLong * @return array * @throws Exception */ - public function makeVisitorProfile(DataTable $visits, $idSite, $visitorId, $segment, $checkForLatLong) + public function makeVisitorProfile(DataTable $visits, $visitorId, $segment) { - $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(); + $this->initVisitorProfile(); - $pageGenerationTimeTotal = 0; - - // aggregate all requested visits info for total_* info /** @var DataTable\Row $visit */ foreach ($visits->getRows() as $visit) { - ++$result['totalVisits']; + ++$this->profile['totalVisits']; - $result['totalVisitDuration'] += $visit->getColumn('visitDuration'); - $result['totalActions'] += $visit->getColumn('actions'); - $result['totalGoalConversions'] += $visit->getColumn('goalConversions'); + $this->profile['totalVisitDuration'] += $visit->getColumn('visitDuration'); + $this->profile['totalActions'] += $visit->getColumn('actions'); + $this->profile['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]); + $this->handleIfGoalAction($action); + $this->handleIfEcommerceAction($action); + $this->handleIfSiteSearchAction($action); + $this->handleIfPageGenerationTime($action); } - $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); + $this->handleGeoLocation($visit); } - if ($result['totalPageViews']) { - $result['averagePageGenerationTime'] = - round($pageGenerationTimeTotal / $result['totalPageViews'], $precision = 2); - } + $this->handleGeoLocationCountries(); + $this->handleGeoLocationContinents(); + $this->handleSiteSearches(); + $this->handleAveragePageGenerationTime(); $formatter = new Formatter(); - $result['totalVisitDurationPretty'] = $formatter->getPrettyTimeFromSeconds($result['totalVisitDuration'], true); + $this->profile['totalVisitDurationPretty'] = $formatter->getPrettyTimeFromSeconds($this->profile['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); + $this->handleVisitsSummary($visits); + $this->handleAdjacentVisitorIds($visits, $visitorId, $segment); // 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'); + $this->enrichVisitsWithFirstActionDatetime($visits); - // 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'); + $this->profile['lastVisits'] = $visits; + $this->profile['userId'] = $visit->getColumn('userId'); - $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; + return $this->profile; } /** @@ -257,23 +120,242 @@ class VisitorProfile if ($referrerType === false || $referrerType == 'direct' ) { - $result = Piwik::translate('Referrers_DirectEntry'); - } else if ($referrerType == 'search') { - $result = $visit->getColumn('referrerName'); + return Piwik::translate('Referrers_DirectEntry'); + } + + if ($referrerType == 'search') { + $referrerName = $visit->getColumn('referrerName'); $keyword = $visit->getColumn('referrerKeyword'); if ($keyword !== false && $keyword != APIReferrers::getKeywordNotDefinedString() ) { - $result .= ' (' . $keyword . ')'; + $referrerName .= ' (' . $keyword . ')'; + } + return $referrerName; + } + + if ($referrerType == 'campaign') { + return Piwik::translate('Referrers_ColumnCampaign') . ' (' . $visit->getColumn('referrerName') . ')'; + } + + return $visit->getColumn('referrerName'); + } + + private function isEcommerceEnabled() + { + return $this->isEcommerceEnabled; + } + + /** + * @param $action + */ + private function handleIfEcommerceAction($action) + { + if (!$this->isEcommerceEnabled()) { + return; + } + if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) { + ++$this->profile['totalEcommerceConversions']; + $this->profile['totalEcommerceRevenue'] += $action['revenue']; + $this->profile['totalEcommerceItems'] += $action['items']; + } else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) { + ++$this->profile['totalAbandonedCarts']; + $this->profile['totalAbandonedCartsRevenue'] += $action['revenue']; + $this->profile['totalAbandonedCartsItems'] += $action['items']; + } + } + + private function handleIfGoalAction($action) + { + if ($action['type'] != 'goal') { + return; + } + $idGoal = $action['goalId']; + $idGoalKey = 'idgoal=' . $idGoal; + + if (!isset($this->profile['totalConversionsByGoal'][$idGoalKey])) { + $this->profile['totalConversionsByGoal'][$idGoalKey] = 0; + } + ++$this->profile['totalConversionsByGoal'][$idGoalKey]; + + if (!empty($action['revenue'])) { + if (!isset($this->profile['totalRevenueByGoal'][$idGoalKey])) { + $this->profile['totalRevenueByGoal'][$idGoalKey] = 0; + } + $this->profile['totalRevenueByGoal'][$idGoalKey] += $action['revenue']; + } + } + + private function handleIfSiteSearchAction($action) + { + if (!isset($action['siteSearchKeyword'])) { + return; + } + $keyword = $action['siteSearchKeyword']; + + if (!isset($this->siteSearchKeywords[$keyword])) { + $this->siteSearchKeywords[$keyword] = 0; + ++$this->profile['totalSearches']; + } + ++$this->siteSearchKeywords[$keyword]; + } + + private function handleGeoLocation(DataTable\Row $visit) + { + // realtime map only checks for latitude + $hasLatitude = $visit->getColumn('latitude') !== false; + if ($hasLatitude) { + $this->profile['hasLatLong'] = true; + } + + $countryCode = $visit->getColumn('countryCode'); + if (!isset($this->countries[$countryCode])) { + $this->countries[$countryCode] = 0; + } + ++$this->countries[$countryCode]; + + $continentCode = $visit->getColumn('continentCode'); + if (!isset($this->continents[$continentCode])) { + $this->continents[$continentCode] = 0; + } + ++$this->continents[$continentCode]; + + if ($countryCode && !array_key_exists($countryCode, $this->cities)) { + $this->cities[$countryCode] = array(); + } + $city = $visit->getColumn('city'); + if (!empty($city)) { + $this->cities[$countryCode][] = $city; + } + } + + private function handleSiteSearches() + { + // sort by visit/action + arsort($this->siteSearchKeywords); + + foreach ($this->siteSearchKeywords as $keyword => $searchCount) { + $this->profile['searches'][] = array('keyword' => $keyword, + 'searches' => $searchCount); + } + } + + private function handleGeoLocationContinents() + { + // sort by visit/action + asort($this->continents); + foreach ($this->continents as $continentCode => $nbVisits) { + $this->profile['continents'][] = array('continent' => $continentCode, + 'nb_visits' => $nbVisits, + 'prettyName' => \Piwik\Plugins\UserCountry\continentTranslate($continentCode)); + } + } + + private function handleGeoLocationCountries() + { + // sort by visit/action + asort($this->countries); + + // transform country/continents/search keywords into something that will look good in XML + $this->profile['countries'] = $this->profile['continents'] = $this->profile['searches'] = array(); + + foreach ($this->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($this->cities[$countryCode])) { + $countryInfo['cities'] = array_unique($this->cities[$countryCode]); } - } else if ($referrerType == 'campaign') { - $result = Piwik::translate('Referrers_ColumnCampaign') . ' (' . $visit->getColumn('referrerName') . ')'; - } else { - $result = $visit->getColumn('referrerName'); + $this->profile['countries'][] = $countryInfo; + } + } + + private function initVisitorProfile() + { + $this->profile['totalVisits'] = 0; + $this->profile['totalVisitDuration'] = 0; + $this->profile['totalActions'] = 0; + $this->profile['totalSearches'] = 0; + $this->profile['totalPageViewsWithTiming'] = 0; + $this->profile['totalGoalConversions'] = 0; + $this->profile['totalConversionsByGoal'] = array(); + $this->profile['hasLatLong'] = false; + + if ($this->isEcommerceEnabled()) { + $this->profile['totalEcommerceConversions'] = 0; + $this->profile['totalEcommerceRevenue'] = 0; + $this->profile['totalEcommerceItems'] = 0; + $this->profile['totalAbandonedCarts'] = 0; + $this->profile['totalAbandonedCartsRevenue'] = 0; + $this->profile['totalAbandonedCartsItems'] = 0; + } + } + + private function handleAveragePageGenerationTime() + { + if ($this->profile['totalPageViewsWithTiming']) { + $this->profile['averagePageGenerationTime'] = + round($this->pageGenerationTimeTotal / $this->profile['totalPageViewsWithTiming'], $precision = 2); + } + } + + private function handleIfPageGenerationTime($action) + { + if (isset($action['generationTime'])) { + $this->pageGenerationTimeTotal += $action['generationTime']; + ++$this->profile['totalPageViewsWithTiming']; } + } + + /** + * @param DataTable $visits + * @param $visitorId + * @param $segment + */ + private function handleAdjacentVisitorIds(DataTable $visits, $visitorId, $segment) + { + // 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. + $rows = $visits->getRows(); + $latestVisitTime = reset($rows)->getColumn('lastActionDateTime'); + + $model = new Model(); + $this->profile['nextVisitorId'] = $model->queryAdjacentVisitorId($this->idSite, $visitorId, $latestVisitTime, $segment, $getNext = true); + $this->profile['previousVisitorId'] = $model->queryAdjacentVisitorId($this->idSite, $visitorId, $latestVisitTime, $segment, $getNext = false); + } + + /** + * @param DataTable $visits + */ + private function handleVisitsSummary(DataTable $visits) + { + $rows = $visits->getRows(); + $this->profile['firstVisit'] = $this->getVisitorProfileVisitSummary(end($rows)); + $this->profile['lastVisit'] = $this->getVisitorProfileVisitSummary(reset($rows)); + $this->profile['visitsAggregated'] = count($rows); + } + + /** + * @param DataTable $visits + * @return DataTable\Row + * @throws Exception + */ + private function enrichVisitsWithFirstActionDatetime(DataTable $visits) + { + $timezone = Site::getTimezoneFor($this->idSite); + foreach ($visits->getRows() as $visit) { + $dateTimeVisitFirstAction = Date::factory($visit->getColumn('firstActionTimestamp'), $timezone); + + $datePretty = $dateTimeVisitFirstAction->getLocalized(self::VISITOR_PROFILE_DATE_FORMAT); + $visit->setColumn('serverDatePrettyFirstAction', $datePretty); - return $result; + $dateTimePretty = $datePretty . ' ' . $visit->getColumn('serverTimePrettyFirstAction'); + $visit->setColumn('serverDateTimePrettyFirstAction', $dateTimePretty); + } } }
\ No newline at end of file diff --git a/plugins/Live/templates/getVisitorProfilePopup.twig b/plugins/Live/templates/getVisitorProfilePopup.twig index 0a25de6a39..afb9551321 100644 --- a/plugins/Live/templates/getVisitorProfilePopup.twig +++ b/plugins/Live/templates/getVisitorProfilePopup.twig @@ -61,7 +61,7 @@ </p> {% endif %} {% if visitorData.averagePageGenerationTime is defined %} - <p title="{{ 'Live_CalculatedOverNPageViews'|translate(visitorData.totalPageViews) }}"> + <p title="{{ 'Live_CalculatedOverNPageViews'|translate(visitorData.totalPageViewsWithTiming) }}"> {{ 'Live_AveragePageGenerationTime'|translate('<strong>' ~ visitorData.averagePageGenerationTime ~ 's</strong>')|raw }} </p> {% endif %} |