getTotalEvolution($totalValue, $lastTotalValue); $minMoversPercent = 1; if ($totalEvolution >= 100) { // eg change from 50 to 150 = 200% $factor = (int) ceil($totalEvolution / 500); $minGrowthPercentPositive = $totalEvolution + ($factor * 40); // min +240% $minGrowthPercentNegative = -70; // min -70% $minDisappearedPercent = 8; // min 12 $minNewPercent = min(($totalEvolution / 100) * 3, 10); // min 6% = min 10 of total visits up to max 10% } elseif ($totalEvolution >= 0) { // eg change from 50 to 75 = 50% $minGrowthPercentPositive = $totalEvolution + 20; // min 70% $minGrowthPercentNegative = -1 * $minGrowthPercentPositive; // min -70% $minDisappearedPercent = 7; $minNewPercent = 5; } else { // eg change from 50 to 25 = -50% $minGrowthPercentNegative = $totalEvolution - 20; // min -70% $minGrowthPercentPositive = abs($minGrowthPercentNegative); // min 70% $minDisappearedPercent = 7; $minNewPercent = 5; } if ($totalValue < 200 && $totalValue != 0) { // force at least a change of 2 visits $minMoversPercent = (int) ceil(2 / ($totalValue / 100)); $minNewPercent = max($minNewPercent, $minMoversPercent); $minDisappearedPercent = max($minDisappearedPercent, $minMoversPercent); } $dataTable = $this->generateInsight($reportMetadata, $period, $date, $lastDate, $metric, $currentReport, $lastReport, $totalValue, $minMoversPercent, $minNewPercent, $minDisappearedPercent, $minGrowthPercentPositive, $minGrowthPercentNegative, $orderBy, $limitIncreaser, $limitDecreaser); $this->addMoversAndShakersMetadata($dataTable, $totalValue, $lastTotalValue); return $dataTable; } /** * Extends an already generated insight report by adding a column "isMoverAndShaker" whether a row is also a * "Mover and Shaker" or not. * * Avoids the need to fetch all reports again when we already have the currentReport/lastReport */ public function markMoversAndShakers(DataTable $insight, $currentReport, $lastReport, $totalValue, $lastTotalValue) { if (!$insight->getRowsCount()) { return; } $limitIncreaser = max($insight->getRowsCount(), 3); $limitDecreaser = max($insight->getRowsCount(), 3); $lastDate = $insight->getMetadata('lastDate'); $date = $insight->getMetadata('date'); $period = $insight->getMetadata('period'); $metric = $insight->getMetadata('metric'); $orderBy = $insight->getMetadata('orderBy'); $reportMetadata = $insight->getMetadata('report'); $shakers = $this->generateMoverAndShaker($reportMetadata, $period, $date, $lastDate, $metric, $currentReport, $lastReport, $totalValue, $lastTotalValue, $orderBy, $limitIncreaser, $limitDecreaser); foreach ($insight->getRows() as $row) { $label = $row->getColumn('label'); if ($shakers->getRowFromLabel($label)) { $row->setColumn('isMoverAndShaker', true); } else { $row->setColumn('isMoverAndShaker', false); } } $this->addMoversAndShakersMetadata($insight, $totalValue, $lastTotalValue); } /** * @param array $reportMetadata * @param string $period * @param string $date * @param string $lastDate * @param string $metric * @param DataTable $currentReport * @param DataTable $lastReport * @param int $totalValue * @param int $minMoversPercent Exclude rows who moved and the difference is not at least min percent * visits of totalVisits. -1 excludes movers. * @param int $minNewPercent Exclude rows who are new and the difference is not at least min percent * visits of totalVisits. -1 excludes all new. * @param int $minDisappearedPercent Exclude rows who are disappeared and the difference is not at least min * percent visits of totalVisits. -1 excludes all disappeared. * @param int $minGrowthPercentPositive The actual growth of a row must be at least percent compared to the * previous value (not total value) * @param int $minGrowthPercentNegative The actual growth of a row must be lower percent compared to the * previous value (not total value) * @param string $orderBy Order by absolute, relative, importance * @param int $limitIncreaser * @param int $limitDecreaser * * @return DataTable */ public function generateInsight($reportMetadata, $period, $date, $lastDate, $metric, $currentReport, $lastReport, $totalValue, $minMoversPercent, $minNewPercent, $minDisappearedPercent, $minGrowthPercentPositive, $minGrowthPercentNegative, $orderBy, $limitIncreaser, $limitDecreaser) { $minChangeMovers = $this->getMinVisits($totalValue, $minMoversPercent); $minIncreaseNew = $this->getMinVisits($totalValue, $minNewPercent); $minDecreaseDisappeared = $this->getMinVisits($totalValue, $minDisappearedPercent); $dataTable = new DataTable(); $dataTable->filter( 'Piwik\Plugins\Insights\DataTable\Filter\Insight', array( $currentReport, $lastReport, $metric, $considerMovers = (-1 !== $minMoversPercent), $considerNew = (-1 !== $minNewPercent), $considerDisappeared = (-1 !== $minDisappearedPercent) ) ); $dataTable->filter( 'Piwik\Plugins\Insights\DataTable\Filter\MinGrowth', array( 'growth_percent_numeric', $minGrowthPercentPositive, $minGrowthPercentNegative ) ); if ($minIncreaseNew) { $dataTable->filter( 'Piwik\Plugins\Insights\DataTable\Filter\ExcludeLowValue', array( 'difference', $minIncreaseNew, 'isNew' ) ); } if ($minChangeMovers) { $dataTable->filter( 'Piwik\Plugins\Insights\DataTable\Filter\ExcludeLowValue', array( 'difference', $minChangeMovers, 'isMover' ) ); } if ($minDecreaseDisappeared) { $dataTable->filter( 'Piwik\Plugins\Insights\DataTable\Filter\ExcludeLowValue', array( 'difference', $minDecreaseDisappeared, 'isDisappeared' ) ); } $dataTable->filter( 'Piwik\Plugins\Insights\DataTable\Filter\OrderBy', array( $this->getOrderByColumn($orderBy), $orderBy === self::ORDER_BY_RELATIVE ? $this->getOrderByColumn(self::ORDER_BY_ABSOLUTE) : $this->getOrderByColumn(self::ORDER_BY_RELATIVE), $metric ) ); $dataTable->filter( 'Piwik\Plugins\Insights\DataTable\Filter\Limit', array( 'growth_percent_numeric', $limitIncreaser, $limitDecreaser ) ); $metricName = $metric; if (!empty($reportMetadata['metrics'][$metric])) { $metricName = $reportMetadata['metrics'][$metric]; } $dataTable->setMetadataValues(array( 'reportName' => $reportMetadata['name'], 'metricName' => $metricName, 'date' => $date, 'lastDate' => $lastDate, 'period' => $period, 'report' => $reportMetadata, 'totalValue' => $totalValue, 'orderBy' => $orderBy, 'metric' => $metric, 'minChangeMovers' => $minChangeMovers, 'minIncreaseNew' => $minIncreaseNew, 'minDecreaseDisappeared' => $minDecreaseDisappeared, 'minGrowthPercentPositive' => $minGrowthPercentPositive, 'minGrowthPercentNegative' => $minGrowthPercentNegative, 'minMoversPercent' => $minMoversPercent, 'minNewPercent' => $minNewPercent, 'minDisappearedPercent' => $minDisappearedPercent )); return $dataTable; } private function getOrderByColumn($orderBy) { if (self::ORDER_BY_RELATIVE == $orderBy) { $orderByColumn = 'growth_percent_numeric'; } elseif (self::ORDER_BY_ABSOLUTE == $orderBy) { $orderByColumn = 'difference'; } elseif (self::ORDER_BY_IMPORTANCE == $orderBy) { $orderByColumn = 'importance'; } else { throw new \Exception('Unsupported orderBy'); } return $orderByColumn; } private function getMinVisits($totalValue, $percent) { if ($percent <= 0) { return 0; } $minVisits = ceil(($totalValue / 100) * $percent); return (int) $minVisits; } private function addMoversAndShakersMetadata(DataTable $dataTable, $totalValue, $lastTotalValue) { $totalEvolution = $this->getTotalEvolution($totalValue, $lastTotalValue); $dataTable->setMetadata('lastTotalValue', $lastTotalValue); $dataTable->setMetadata('evolutionTotal', $totalEvolution); $dataTable->setMetadata('evolutionDifference', $totalValue - $lastTotalValue); } private function getTotalEvolution($totalValue, $lastTotalValue) { return Piwik::getPercentageSafe($totalValue - $lastTotalValue, $lastTotalValue, 1); } }