PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/Insights/API.php

https://github.com/CodeYellowBV/piwik
PHP | 345 lines | 174 code | 54 blank | 117 comment | 9 complexity | 5450af3cdf35367d7f28213b3abe519d MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\Plugins\Insights;
  10. use Piwik\API\Request as ApiRequest;
  11. use Piwik\DataTable;
  12. use Piwik\Piwik;
  13. /**
  14. * API for plugin Insights
  15. *
  16. * @method static \Piwik\Plugins\Insights\API getInstance()
  17. */
  18. class API extends \Piwik\Plugin\API
  19. {
  20. /**
  21. * Include only 'movers' which are existing in the current and past report.
  22. */
  23. const FILTER_BY_MOVERS = 'movers';
  24. /**
  25. * Include only 'new' rows which were not existing in the past report.
  26. */
  27. const FILTER_BY_NEW = 'new';
  28. /**
  29. * Include only 'disappeared' rows which were existing in the past report but no longer in the current report.
  30. */
  31. const FILTER_BY_DISAPPEARED = 'disappeared';
  32. /**
  33. * @var Model
  34. */
  35. private $model;
  36. protected function __construct()
  37. {
  38. parent::__construct();
  39. $this->model = new Model();
  40. }
  41. private function getOverviewReports()
  42. {
  43. $reports = array();
  44. /**
  45. * Triggered to gather all reports to be displayed in the "Insight" and "Movers And Shakers" overview reports.
  46. * Plugins that want to add new reports to the overview should subscribe to this event and add reports to the
  47. * incoming array. API parameters can be configured as an array optionally.
  48. *
  49. * **Example**
  50. *
  51. * public function addReportToInsightsOverview(&$reports)
  52. * {
  53. * $reports['Actions_getPageUrls'] = array();
  54. * $reports['Actions_getDownloads'] = array('flat' => 1, 'minGrowthPercent' => 60);
  55. * }
  56. *
  57. * @param array &$reports An array containing a report unique id as key and an array of API parameters as
  58. * values.
  59. */
  60. Piwik::postEvent('Insights.addReportToOverview', array(&$reports));
  61. return $reports;
  62. }
  63. /**
  64. * Detects whether insights can be generated for this date/period combination or not.
  65. * @param string $date eg 'today', '2012-12-12'
  66. * @param string $period eg 'day' or 'week'
  67. *
  68. * @return bool
  69. */
  70. public function canGenerateInsights($date, $period)
  71. {
  72. Piwik::checkUserHasSomeViewAccess();
  73. try {
  74. $model = new Model();
  75. $lastDate = $model->getLastDate($date, $period, 1);
  76. } catch (\Exception $e) {
  77. return false;
  78. }
  79. if (empty($lastDate)) {
  80. return false;
  81. }
  82. return true;
  83. }
  84. /**
  85. * Generates insights for a set of reports. Plugins can add their own reports to be included in the insights
  86. * overview by listening to the {@hook Insights.addReportToOverview} event.
  87. *
  88. * @param int $idSite
  89. * @param string $period
  90. * @param string $date
  91. * @param bool|string $segment
  92. *
  93. * @return DataTable\Map A map containing a dataTable for each insight report. See {@link getInsights()} for more
  94. * information
  95. */
  96. public function getInsightsOverview($idSite, $period, $date, $segment = false)
  97. {
  98. Piwik::checkUserHasViewAccess($idSite);
  99. $defaultParams = array(
  100. 'limitIncreaser' => 3,
  101. 'limitDecreaser' => 3,
  102. 'minImpactPercent' => 1,
  103. 'minGrowthPercent' => 25,
  104. );
  105. $map = $this->generateOverviewReport('getInsights', $idSite, $period, $date, $segment, $defaultParams);
  106. return $map;
  107. }
  108. /**
  109. * Detects the movers and shakers for a set of reports. Plugins can add their own reports to be included in this
  110. * overview by listening to the {@hook Insights.addReportToOverview} event.
  111. *
  112. * @param int $idSite
  113. * @param string $period
  114. * @param string $date
  115. * @param bool|string $segment
  116. *
  117. * @return DataTable\Map A map containing a dataTable for each movers and shakers report. See
  118. * {@link getMoversAndShakers()} for more information
  119. */
  120. public function getMoversAndShakersOverview($idSite, $period, $date, $segment = false)
  121. {
  122. Piwik::checkUserHasViewAccess($idSite);
  123. $defaultParams = array(
  124. 'limitIncreaser' => 4,
  125. 'limitDecreaser' => 4
  126. );
  127. $map = $this->generateOverviewReport('getMoversAndShakers', $idSite, $period, $date, $segment, $defaultParams);
  128. return $map;
  129. }
  130. private function generateOverviewReport($method, $idSite, $period, $date, $segment, array $defaultParams)
  131. {
  132. $tableManager = DataTable\Manager::getInstance();
  133. /** @var DataTable[] $tables */
  134. $tables = array();
  135. foreach ($this->getOverviewReports() as $reportId => $reportParams) {
  136. if (!empty($reportParams)) {
  137. foreach ($defaultParams as $key => $defaultParam) {
  138. if (!array_key_exists($key, $reportParams)) {
  139. $reportParams[$key] = $defaultParam;
  140. }
  141. }
  142. }
  143. $firstTableId = $tableManager->getMostRecentTableId();
  144. $table = $this->requestApiMethod($method, $idSite, $period, $date, $reportId, $segment, $reportParams);
  145. $reportTableIds[] = $table->getId();
  146. $tableManager->deleteTablesExceptIgnored($reportTableIds, $firstTableId);
  147. $tables[] = $table;
  148. }
  149. $map = new DataTable\Map();
  150. foreach ($tables as $table) {
  151. $map->addTable($table, $table->getMetadata('reportName'));
  152. }
  153. return $map;
  154. }
  155. /**
  156. * Detects the movers and shakers of a given date / report combination. A mover and shakers has an higher impact
  157. * than other rows on average. For instance if a sites pageviews increase by 10% a page that increased by 40% at the
  158. * same time contributed significantly more to the success than the average of 10%.
  159. *
  160. * @param int $idSite
  161. * @param string $period
  162. * @param string $date
  163. * @param string $reportUniqueId eg 'Actions_getPageUrls'. An id like 'Goals_getVisitsUntilConversion_idGoal--4' works as well.
  164. * @param bool|string $segment
  165. * @param int $comparedToXPeriods
  166. * @param int $limitIncreaser Value '0' ignores all increasers
  167. * @param int $limitDecreaser Value '0' ignores all decreasers
  168. *
  169. * @return DataTable
  170. *
  171. * @throws \Exception In case a report having the given ID does not exist
  172. * @throws \Exception In case the report exists but does not return a dataTable
  173. */
  174. public function getMoversAndShakers($idSite, $period, $date, $reportUniqueId, $segment = false,
  175. $comparedToXPeriods = 1, $limitIncreaser = 4, $limitDecreaser = 4)
  176. {
  177. Piwik::checkUserHasViewAccess(array($idSite));
  178. $metric = 'nb_visits';
  179. $orderBy = InsightReport::ORDER_BY_ABSOLUTE;
  180. $reportMetadata = $this->model->getReportByUniqueId($idSite, $reportUniqueId);
  181. if (empty($reportMetadata)) {
  182. throw new \Exception('A report having the ID ' . $reportUniqueId . ' does not exist');
  183. }
  184. $totalValue = $this->model->getTotalValue($idSite, $period, $date, $metric);
  185. $currentReport = $this->model->requestReport($idSite, $period, $date, $reportUniqueId, $metric, $segment);
  186. $this->checkReportIsValid($currentReport);
  187. $lastDate = $this->model->getLastDate($date, $period, $comparedToXPeriods);
  188. $lastTotalValue = $this->model->getTotalValue($idSite, $period, $lastDate, $metric);
  189. $lastReport = $this->model->requestReport($idSite, $period, $lastDate, $reportUniqueId, $metric, $segment);
  190. $this->checkReportIsValid($lastReport);
  191. $insight = new InsightReport();
  192. return $insight->generateMoverAndShaker($reportMetadata, $period, $date, $lastDate, $metric, $currentReport, $lastReport, $totalValue, $lastTotalValue, $orderBy, $limitIncreaser, $limitDecreaser);
  193. }
  194. /**
  195. * Generates insights by comparing the report for a given date/period with a different date and calculating the
  196. * difference. The API can exclude rows which growth is not good enough or did not have enough impact.
  197. *
  198. * @param int $idSite
  199. * @param string $period
  200. * @param string $date
  201. * @param string $reportUniqueId eg 'Actions_getPageUrls'. An id like 'Goals_getVisitsUntilConversion_idGoal--4' works as well.
  202. * @param bool|string $segment
  203. * @param int $limitIncreaser Value '0' ignores all increasers
  204. * @param int $limitDecreaser Value '0' ignores all decreasers
  205. * @param string $filterBy By default all rows will be ignored. If given only 'movers', 'new' or 'disappeared' will be returned.
  206. * @param int $minImpactPercent The minimum impact in percent. Eg '2%' of 1000 visits means the change /
  207. * increase / decrease has to be at least 20 visits. Usually the '2%' are based on the total
  208. * amount of visits but for reports having way less visits the metric total is used. Eg A page
  209. * has 1000 visits but only 100 visits having keywords. In this case a minimum impact of '2%' evaluates to 2 and not 20.
  210. * @param int $minGrowthPercent The amount of percent a row has to increase or decrease at least compared to the previous period.
  211. * If value is '20' the growth has to be either at least '+20%' or '-20%' and lower.
  212. * @param int $comparedToXPeriods The report will be compared to X periods before.
  213. * @param string $orderBy Orders the rows by 'absolute', 'relative' or 'importance'.
  214. *
  215. * @return DataTable
  216. *
  217. * @throws \Exception In case a report having the given ID does not exist
  218. * @throws \Exception In case the report exists but does not return a dataTable
  219. */
  220. public function getInsights(
  221. $idSite, $period, $date, $reportUniqueId, $segment = false, $limitIncreaser = 5, $limitDecreaser = 5,
  222. $filterBy = '', $minImpactPercent = 2, $minGrowthPercent = 20,
  223. $comparedToXPeriods = 1, $orderBy = 'absolute')
  224. {
  225. Piwik::checkUserHasViewAccess(array($idSite));
  226. $metric = 'nb_visits';
  227. $reportMetadata = $this->model->getReportByUniqueId($idSite, $reportUniqueId);
  228. if (empty($reportMetadata)) {
  229. throw new \Exception('A report having the ID ' . $reportUniqueId . ' does not exist');
  230. }
  231. $totalValue = $this->model->getTotalValue($idSite, $period, $date, $metric);
  232. $currentReport = $this->model->requestReport($idSite, $period, $date, $reportUniqueId, $metric, $segment);
  233. $this->checkReportIsValid($currentReport);
  234. $lastDate = $this->model->getLastDate($date, $period, $comparedToXPeriods);
  235. $lastTotalValue = $this->model->getTotalValue($idSite, $period, $lastDate, $metric);
  236. $lastReport = $this->model->requestReport($idSite, $period, $lastDate, $reportUniqueId, $metric, $segment);
  237. $this->checkReportIsValid($lastReport);
  238. $minGrowthPercentPositive = abs($minGrowthPercent);
  239. $minGrowthPercentNegative = -1 * $minGrowthPercentPositive;
  240. $relevantTotal = $this->model->getRelevantTotalValue($currentReport, $metric, $totalValue);
  241. $minMoversPercent = -1;
  242. $minNewPercent = -1;
  243. $minDisappearedPercent = -1;
  244. switch ($filterBy) {
  245. case self::FILTER_BY_MOVERS:
  246. $minMoversPercent = $minImpactPercent;
  247. break;
  248. case self::FILTER_BY_NEW:
  249. $minNewPercent = $minImpactPercent;
  250. break;
  251. case self::FILTER_BY_DISAPPEARED:
  252. $minDisappearedPercent = $minImpactPercent;
  253. break;
  254. default:
  255. $minMoversPercent = $minImpactPercent;
  256. $minNewPercent = $minImpactPercent;
  257. $minDisappearedPercent = $minImpactPercent;
  258. }
  259. $insight = new InsightReport();
  260. $table = $insight->generateInsight($reportMetadata, $period, $date, $lastDate, $metric, $currentReport, $lastReport, $relevantTotal, $minMoversPercent, $minNewPercent, $minDisappearedPercent, $minGrowthPercentPositive, $minGrowthPercentNegative, $orderBy, $limitIncreaser, $limitDecreaser);
  261. $insight->markMoversAndShakers($table, $currentReport, $lastReport, $totalValue, $lastTotalValue);
  262. return $table;
  263. }
  264. private function checkReportIsValid($report)
  265. {
  266. if (!($report instanceof DataTable)) {
  267. throw new \Exception('Insight can be only generated for reports returning a dataTable');
  268. }
  269. }
  270. private function requestApiMethod($method, $idSite, $period, $date, $reportId, $segment, $additionalParams)
  271. {
  272. $params = array(
  273. 'method' => 'Insights.' . $method,
  274. 'idSite' => $idSite,
  275. 'date' => $date,
  276. 'period' => $period,
  277. 'format' => 'original',
  278. 'reportUniqueId' => $reportId,
  279. );
  280. if (!empty($segment)) {
  281. $params['segment'] = $segment;
  282. }
  283. if (!empty($additionalParams)) {
  284. foreach ($additionalParams as $key => $value) {
  285. $params[$key] = $value;
  286. }
  287. }
  288. $request = new ApiRequest($params);
  289. return $request->process();
  290. }
  291. }