piwik /plugins/Insights/API.php

Language PHP Lines 346
MD5 Hash 5450af3cdf35367d7f28213b3abe519d Estimated Cost $4,281 (why?)
Repository https://github.com/CodeYellowBV/piwik.git View Raw File View Project SPDX
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
<?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\Insights;

use Piwik\API\Request as ApiRequest;
use Piwik\DataTable;
use Piwik\Piwik;

/**
 * API for plugin Insights
 *
 * @method static \Piwik\Plugins\Insights\API getInstance()
 */
class API extends \Piwik\Plugin\API
{
    /**
     * Include only 'movers' which are existing in the current and past report.
     */
    const FILTER_BY_MOVERS = 'movers';

    /**
     * Include only 'new' rows which were not existing in the past report.
     */
    const FILTER_BY_NEW = 'new';

    /**
     * Include only 'disappeared' rows which were existing in the past report but no longer in the current report.
     */
    const FILTER_BY_DISAPPEARED = 'disappeared';

    /**
     * @var Model
     */
    private $model;

    protected function __construct()
    {
        parent::__construct();

        $this->model = new Model();
    }

    private function getOverviewReports()
    {
        $reports = array();

        /**
         * Triggered to gather all reports to be displayed in the "Insight" and "Movers And Shakers" overview reports.
         * Plugins that want to add new reports to the overview should subscribe to this event and add reports to the
         * incoming array. API parameters can be configured as an array optionally.
         *
         * **Example**
         *
         *     public function addReportToInsightsOverview(&$reports)
         *     {
         *         $reports['Actions_getPageUrls']  = array();
         *         $reports['Actions_getDownloads'] = array('flat' => 1, 'minGrowthPercent' => 60);
         *     }
         *
         * @param array &$reports An array containing a report unique id as key and an array of API parameters as
         *                        values.
         */
        Piwik::postEvent('Insights.addReportToOverview', array(&$reports));

        return $reports;
    }

    /**
     * Detects whether insights can be generated for this date/period combination or not.
     * @param string $date     eg 'today', '2012-12-12'
     * @param string $period   eg 'day' or 'week'
     *
     * @return bool
     */
    public function canGenerateInsights($date, $period)
    {
        Piwik::checkUserHasSomeViewAccess();

        try {
            $model    = new Model();
            $lastDate = $model->getLastDate($date, $period, 1);
        } catch (\Exception $e) {
            return false;
        }

        if (empty($lastDate)) {
            return false;
        }

        return true;
    }

    /**
     * Generates insights for a set of reports. Plugins can add their own reports to be included in the insights
     * overview by listening to the {@hook Insights.addReportToOverview} event.
     *
     * @param int $idSite
     * @param string $period
     * @param string $date
     * @param bool|string $segment
     *
     * @return DataTable\Map   A map containing a dataTable for each insight report. See {@link getInsights()} for more
     *                         information
     */
    public function getInsightsOverview($idSite, $period, $date, $segment = false)
    {
        Piwik::checkUserHasViewAccess($idSite);

        $defaultParams = array(
            'limitIncreaser' => 3,
            'limitDecreaser' => 3,
            'minImpactPercent' => 1,
            'minGrowthPercent' => 25,
        );

        $map = $this->generateOverviewReport('getInsights', $idSite, $period, $date, $segment, $defaultParams);

        return $map;
    }

    /**
     * Detects the movers and shakers for a set of reports. Plugins can add their own reports to be included in this
     * overview by listening to the {@hook Insights.addReportToOverview} event.
     *
     * @param int $idSite
     * @param string $period
     * @param string $date
     * @param bool|string $segment
     *
     * @return DataTable\Map   A map containing a dataTable for each movers and shakers report. See
     *                         {@link getMoversAndShakers()} for more information
     */
    public function getMoversAndShakersOverview($idSite, $period, $date, $segment = false)
    {
        Piwik::checkUserHasViewAccess($idSite);

        $defaultParams = array(
            'limitIncreaser' => 4,
            'limitDecreaser' => 4
        );

        $map = $this->generateOverviewReport('getMoversAndShakers', $idSite, $period, $date, $segment, $defaultParams);

        return $map;
    }

    private function generateOverviewReport($method, $idSite, $period, $date, $segment, array $defaultParams)
    {
        $tableManager = DataTable\Manager::getInstance();

        /** @var DataTable[] $tables */
        $tables = array();
        foreach ($this->getOverviewReports() as $reportId => $reportParams) {
            if (!empty($reportParams)) {
                foreach ($defaultParams as $key => $defaultParam) {
                    if (!array_key_exists($key, $reportParams)) {
                        $reportParams[$key] = $defaultParam;
                    }
                }
            }

            $firstTableId     = $tableManager->getMostRecentTableId();
            $table            = $this->requestApiMethod($method, $idSite, $period, $date, $reportId, $segment, $reportParams);
            $reportTableIds[] = $table->getId();
            $tableManager->deleteTablesExceptIgnored($reportTableIds, $firstTableId);

            $tables[] = $table;
        }

        $map = new DataTable\Map();

        foreach ($tables as $table) {
            $map->addTable($table, $table->getMetadata('reportName'));
        }

        return $map;
    }

    /**
     * Detects the movers and shakers of a given date / report combination. A mover and shakers has an higher impact
     * than other rows on average. For instance if a sites pageviews increase by 10% a page that increased by 40% at the
     * same time contributed significantly more to the success than the average of 10%.
     *
     * @param int $idSite
     * @param string $period
     * @param string $date
     * @param string $reportUniqueId   eg 'Actions_getPageUrls'. An id like 'Goals_getVisitsUntilConversion_idGoal--4' works as well.
     * @param bool|string $segment
     * @param int $comparedToXPeriods
     * @param int $limitIncreaser      Value '0' ignores all increasers
     * @param int $limitDecreaser      Value '0' ignores all decreasers
     *
     * @return DataTable
     *
     * @throws \Exception In case a report having the given ID does not exist
     * @throws \Exception In case the report exists but does not return a dataTable
     */
    public function getMoversAndShakers($idSite, $period, $date, $reportUniqueId, $segment = false,
                                        $comparedToXPeriods = 1, $limitIncreaser = 4, $limitDecreaser = 4)
    {
        Piwik::checkUserHasViewAccess(array($idSite));

        $metric  = 'nb_visits';
        $orderBy = InsightReport::ORDER_BY_ABSOLUTE;

        $reportMetadata = $this->model->getReportByUniqueId($idSite, $reportUniqueId);

        if (empty($reportMetadata)) {
            throw new \Exception('A report having the ID ' . $reportUniqueId .  ' does not exist');
        }

        $totalValue     = $this->model->getTotalValue($idSite, $period, $date, $metric);
        $currentReport  = $this->model->requestReport($idSite, $period, $date, $reportUniqueId, $metric, $segment);
        $this->checkReportIsValid($currentReport);

        $lastDate       = $this->model->getLastDate($date, $period, $comparedToXPeriods);
        $lastTotalValue = $this->model->getTotalValue($idSite, $period, $lastDate, $metric);
        $lastReport     = $this->model->requestReport($idSite, $period, $lastDate, $reportUniqueId, $metric, $segment);
        $this->checkReportIsValid($lastReport);

        $insight = new InsightReport();
        return $insight->generateMoverAndShaker($reportMetadata, $period, $date, $lastDate, $metric, $currentReport, $lastReport, $totalValue, $lastTotalValue, $orderBy, $limitIncreaser, $limitDecreaser);
    }

    /**
     * Generates insights by comparing the report for a given date/period with a different date and calculating the
     * difference. The API can exclude rows which growth is not good enough or did not have enough impact.
     *
     * @param int $idSite
     * @param string $period
     * @param string $date
     * @param string $reportUniqueId   eg 'Actions_getPageUrls'. An id like 'Goals_getVisitsUntilConversion_idGoal--4' works as well.
     * @param bool|string $segment
     * @param int $limitIncreaser      Value '0' ignores all increasers
     * @param int $limitDecreaser      Value '0' ignores all decreasers
     * @param string $filterBy         By default all rows will be ignored. If given only 'movers', 'new' or 'disappeared' will be returned.
     * @param int $minImpactPercent    The minimum impact in percent. Eg '2%' of 1000 visits means the change /
     *                                 increase / decrease has to be at least 20 visits. Usually the '2%' are based on the total
     *                                 amount of visits but for reports having way less visits the metric total is used. Eg A page
     *                                 has 1000 visits but only 100 visits having keywords. In this case a minimum impact of '2%' evaluates to 2 and not 20.
     * @param int $minGrowthPercent    The amount of percent a row has to increase or decrease at least compared to the previous period.
     *                                 If value is '20' the growth has to be either at least '+20%' or '-20%' and lower.
     * @param int $comparedToXPeriods  The report will be compared to X periods before.
     * @param string $orderBy          Orders the rows by 'absolute', 'relative' or 'importance'.
     *
     * @return DataTable
     *
     * @throws \Exception In case a report having the given ID does not exist
     * @throws \Exception In case the report exists but does not return a dataTable
     */
    public function getInsights(
        $idSite, $period, $date, $reportUniqueId, $segment = false, $limitIncreaser = 5, $limitDecreaser = 5,
        $filterBy = '', $minImpactPercent = 2, $minGrowthPercent = 20,
        $comparedToXPeriods = 1, $orderBy = 'absolute')
    {
        Piwik::checkUserHasViewAccess(array($idSite));

        $metric = 'nb_visits';

        $reportMetadata = $this->model->getReportByUniqueId($idSite, $reportUniqueId);

        if (empty($reportMetadata)) {
            throw new \Exception('A report having the ID ' . $reportUniqueId .  ' does not exist');
        }

        $totalValue     = $this->model->getTotalValue($idSite, $period, $date, $metric);
        $currentReport  = $this->model->requestReport($idSite, $period, $date, $reportUniqueId, $metric, $segment);
        $this->checkReportIsValid($currentReport);

        $lastDate       = $this->model->getLastDate($date, $period, $comparedToXPeriods);
        $lastTotalValue = $this->model->getTotalValue($idSite, $period, $lastDate, $metric);
        $lastReport     = $this->model->requestReport($idSite, $period, $lastDate, $reportUniqueId, $metric, $segment);
        $this->checkReportIsValid($lastReport);

        $minGrowthPercentPositive = abs($minGrowthPercent);
        $minGrowthPercentNegative = -1 * $minGrowthPercentPositive;

        $relevantTotal = $this->model->getRelevantTotalValue($currentReport, $metric, $totalValue);

        $minMoversPercent      = -1;
        $minNewPercent         = -1;
        $minDisappearedPercent = -1;

        switch ($filterBy) {
            case self::FILTER_BY_MOVERS:
                $minMoversPercent = $minImpactPercent;
                break;
            case self::FILTER_BY_NEW:
                $minNewPercent = $minImpactPercent;
                break;
            case self::FILTER_BY_DISAPPEARED:
                $minDisappearedPercent = $minImpactPercent;
                break;
            default:
                $minMoversPercent      = $minImpactPercent;
                $minNewPercent         = $minImpactPercent;
                $minDisappearedPercent = $minImpactPercent;
        }

        $insight = new InsightReport();
        $table   = $insight->generateInsight($reportMetadata, $period, $date, $lastDate, $metric, $currentReport, $lastReport, $relevantTotal, $minMoversPercent, $minNewPercent, $minDisappearedPercent, $minGrowthPercentPositive, $minGrowthPercentNegative, $orderBy, $limitIncreaser, $limitDecreaser);
        $insight->markMoversAndShakers($table, $currentReport, $lastReport, $totalValue, $lastTotalValue);

        return $table;
    }

    private function checkReportIsValid($report)
    {
        if (!($report instanceof DataTable)) {
            throw new \Exception('Insight can be only generated for reports returning a dataTable');
        }
    }

    private function requestApiMethod($method, $idSite, $period, $date, $reportId, $segment, $additionalParams)
    {
        $params = array(
            'method' => 'Insights.' . $method,
            'idSite' => $idSite,
            'date'   => $date,
            'period' => $period,
            'format' => 'original',
            'reportUniqueId' => $reportId,
        );

        if (!empty($segment)) {
            $params['segment'] = $segment;
        }

        if (!empty($additionalParams)) {
            foreach ($additionalParams as $key => $value) {
                $params[$key] = $value;
            }
        }

        $request = new ApiRequest($params);
        return $request->process();
    }

}
Back to Top