Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge remote-tracking branch 'origin/master'

  • Loading branch information...
commit 91f757858a58ffbc6b52dfd04f7fe70d5ac11313 2 parents bc0b39e + 2e1cb7b
@mattab mattab authored
View
5 LEGALNOTICE
@@ -243,6 +243,11 @@ THIRD-PARTY CONTENT
Link: http://findicons.com/icon/94051/tools_wizard?id=396912
License: GNU/GPL
+ Name: plugins/Insights/images/idea.png
+ Link: https://www.iconfinder.com/icons/6074/brainstorm_bulb_idea_jabber_light_icon
+ License: GPL
+ By: Alessandro Rei - http://www.kde-look.org/usermanager/search.php?username=mentalrey
+
Notes:
- the "New BSD" license refers to either the "Modified BSD" and "Simplified BSD"
licenses (2- or 3-clause), which are GPL compatible.
View
2  lang/en.json
@@ -1055,12 +1055,14 @@
"DatePeriodCombinationNotSupported": "It is not possible to generate insights for this date and period combination.",
"ControlGrowthDescription": "Minimum growth of",
"ControlComparedToDescription": "compared to the",
+ "ControlFilterByDescription": "Show all, only movers, only new or only disappeared",
"DayComparedToPreviousDay": "previous day",
"DayComparedToPreviousWeek": "same day in the previous week",
"DayComparedToPreviousYear": "same day in the previous year",
"WeekComparedToPreviousWeek": "previous week",
"MonthComparedToPreviousMonth": "previous month",
"MonthComparedToPreviousYear": "same month in the previous year",
+ "YearComparedToPreviousYear": "previous year",
"Filter": "Filter",
"FilterOnlyMovers": "Only movers",
"FilterOnlyNew": "Only new",
View
10 plugins/Actions/Actions.php
@@ -42,11 +42,19 @@ public function getListHooksRegistered()
'API.getSegmentDimensionMetadata' => 'getSegmentsMetadata',
'ViewDataTable.configure' => 'configureViewDataTable',
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
- 'AssetManager.getJavaScriptFiles' => 'getJsFiles'
+ 'AssetManager.getJavaScriptFiles' => 'getJsFiles',
+ 'Insights.addReportToOverview' => 'addReportToInsightsOverview'
);
return $hooks;
}
+ public function addReportToInsightsOverview(&$reports)
+ {
+ $reports['Actions_getPageUrls'] = array();
+ $reports['Actions_getPageTitles'] = array();
+ $reports['Actions_getDownloads'] = array('flat' => 1);
+ }
+
public function getStylesheetFiles(&$stylesheets)
{
$stylesheets[] = "plugins/Actions/stylesheets/dataTableActions.less";
View
4 plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js
@@ -323,7 +323,7 @@
// allow plugins to modify tracking link code
var eventData = {code: result};
- $(TrackingCodeGeneratorSingleton).trigger('customizeTrackerLink', result);
+ $(TrackingCodeGeneratorSingleton).trigger('customizeTrackerLink', eventData);
result = eventData.code;
result = result.replace(/[&]/g, "&");
@@ -412,4 +412,4 @@
);
});
-}(jQuery, require));
+}(jQuery, require));
View
159 plugins/Insights/API.php
@@ -24,30 +24,25 @@
*/
class API extends \Piwik\Plugin\API
{
- const FILTER_BY_NEW = 'new';
+ /**
+ * Include only 'movers' which are existing in the current and past report.
+ */
const FILTER_BY_MOVERS = 'movers';
- const FILTER_BY_DISAPPEARED = 'disappeared';
/**
- * @var Model
+ * Include only 'new' rows which were not existing in the past report.
*/
- private $model;
+ const FILTER_BY_NEW = 'new';
/**
- * Those reports will be included in the insight / moversAndShakers overview reports.
- * You can configure any API parameter for each report such as "flat", "limitIncreaser", "minGrowth", ...
- * @var array
+ * Include only 'disappeared' rows which were existing in the past report but no longer in the current report.
*/
- private $reportIds = array(
- 'Actions_getPageUrls' => array(),
- 'Actions_getPageTitles' => array(),
- 'Actions_getDownloads' => array('flat' => 1),
- 'Referrers_getWebsites' => array(),
- 'Referrers_getCampaigns' => array(),
- 'Referrers_getSocials' => array(),
- 'Referrers_getSearchEngines' => array(),
- 'UserCountry_getCountry' => array(),
- );
+ const FILTER_BY_DISAPPEARED = 'disappeared';
+
+ /**
+ * @var Model
+ */
+ private $model;
protected function __construct()
{
@@ -56,6 +51,38 @@ protected function __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();
@@ -74,6 +101,18 @@ public function canGenerateInsights($date, $period)
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);
@@ -90,6 +129,18 @@ public function getInsightsOverview($idSite, $period, $date, $segment = false)
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);
@@ -110,10 +161,12 @@ private function generateOverviewReport($method, $idSite, $period, $date, $segme
/** @var DataTable[] $tables */
$tables = array();
- foreach ($this->reportIds as $reportId => $reportParams) {
- foreach ($defaultParams as $key => $defaultParam) {
- if (!array_key_exists($key, $reportParams)) {
- $reportParams[$key] = $defaultParam;
+ foreach ($this->getOverviewReports() as $reportId => $reportParams) {
+ if (!empty($reportParams)) {
+ foreach ($defaultParams as $key => $defaultParam) {
+ if (!array_key_exists($key, $reportParams)) {
+ $reportParams[$key] = $defaultParam;
+ }
}
}
@@ -134,6 +187,25 @@ private function generateOverviewReport($method, $idSite, $period, $date, $segme
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)
{
@@ -144,17 +216,49 @@ public function getMoversAndShakers($idSite, $period, $date, $reportUniqueId, $s
$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,
@@ -166,12 +270,18 @@ public function getInsights(
$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;
@@ -205,6 +315,13 @@ public function getInsights(
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(
View
3  plugins/Insights/DataTable/Filter/ExcludeLowValue.php
@@ -10,6 +10,9 @@
use Piwik\DataTable;
+/**
+ * Removes all rows whose value is too low.
+ */
class ExcludeLowValue extends DataTable\BaseFilter
{
private $minimumValue;
View
20 plugins/Insights/DataTable/Filter/Limit.php
@@ -9,17 +9,21 @@
namespace Piwik\Plugins\Insights\DataTable\Filter;
use Piwik\DataTable\BaseFilter;
+/**
+ * Limits the number of positive and negative values. A value is considered as positive if the value of $columnToRead
+ * is 0 or higher. A value is considered as negative in all other cases (< 0).
+ */
class Limit extends BaseFilter
{
- private $limitIncreaser;
- private $limitDecreaser;
+ private $limitPositive;
+ private $limitNegative;
private $columnToRead;
- public function __construct($table, $columnToRead, $limitIncreaser, $limitDecreaser)
+ public function __construct($table, $columnToRead, $limitPositiveValues, $limitNegativeValues)
{
- $this->columnToRead = $columnToRead;
- $this->limitIncreaser = (int) $limitIncreaser;
- $this->limitDecreaser = (int) $limitDecreaser;
+ $this->columnToRead = $columnToRead;
+ $this->limitPositive = (int) $limitPositiveValues;
+ $this->limitNegative = (int) $limitNegativeValues;
}
public function filter($table)
@@ -33,14 +37,14 @@ public function filter($table)
$countIncreaser++;
- if ($countIncreaser > $this->limitIncreaser) {
+ if ($countIncreaser > $this->limitPositive) {
$table->deleteRow($key);
}
} else {
$countDecreaser++;
- if ($countDecreaser > $this->limitDecreaser) {
+ if ($countDecreaser > $this->limitNegative) {
$table->deleteRow($key);
}
View
21 plugins/Insights/DataTable/Filter/MinGrowth.php
@@ -11,22 +11,27 @@
use Piwik\DataTable\BaseFilter;
use Piwik\DataTable;
+/**
+ * A row will be deleted if a positive value of $columnToRead is lower than the $minPositiveValue or if the negative
+ * value of $columnToRead is higher than the $minNegativeValue.
+ * That means a row will be deleted if the value is between $minNegativeValue and $minPositiveValue.
+ */
class MinGrowth extends BaseFilter
{
- private $minPositiveGrowth;
- private $minNegativeGrowth;
+ private $minPositiveValue;
+ private $minNegativeValue;
private $columnToRead;
- public function __construct($table, $columnToRead, $minPositiveGrowth, $minNegativeGrowth)
+ public function __construct($table, $columnToRead, $minPositiveValue, $minNegativeValue)
{
$this->columnToRead = $columnToRead;
- $this->minPositiveGrowth = $minPositiveGrowth;
- $this->minNegativeGrowth = $minNegativeGrowth;
+ $this->minPositiveValue = $minPositiveValue;
+ $this->minNegativeValue = $minNegativeValue;
}
public function filter($table)
{
- if (!$this->minPositiveGrowth && !$this->minNegativeGrowth) {
+ if (!$this->minPositiveValue && !$this->minNegativeValue) {
return;
}
@@ -34,9 +39,9 @@ public function filter($table)
$growthNumeric = $row->getColumn($this->columnToRead);
- if ($growthNumeric >= $this->minPositiveGrowth && $growthNumeric >= 0) {
+ if ($growthNumeric >= $this->minPositiveValue && $growthNumeric >= 0) {
continue;
- } elseif ($growthNumeric <= $this->minNegativeGrowth && $growthNumeric < 0) {
+ } elseif ($growthNumeric <= $this->minNegativeValue && $growthNumeric < 0) {
continue;
}
View
9 plugins/Insights/DataTable/Filter/OrderBy.php
@@ -11,6 +11,15 @@
use Piwik\DataTable\BaseFilter;
use Piwik\DataTable\Row;
+/**
+ * Goal is to list all positive values first (the higher the better) and then all negative values (the lower the better).
+ *
+ * 40%
+ * 20%
+ * 0%
+ * -40%
+ * -20%
+ */
class OrderBy extends BaseFilter
{
private $columnsToCheck;
View
1  plugins/Insights/InsightReport.php
@@ -136,6 +136,7 @@ public function markMoversAndShakers(DataTable $insight, $currentReport, $lastRe
* @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)
View
5 plugins/Insights/images/LICENSE
@@ -1,5 +0,0 @@
-idea.png
-========
-Link: https://www.iconfinder.com/icons/6074/brainstorm_bulb_idea_jabber_light_icon
-License: GPL
-By: Alessandro Rei - http://www.kde-look.org/usermanager/search.php?username=mentalrey
View
9 plugins/Insights/stylesheets/insightVisualization.less
@@ -6,6 +6,11 @@
.insightsDataTable {
+ .controls {
+ padding: 10px;
+ padding-bottom: 0px;
+ }
+
.controlSeparator {
height: 1px;
border: 0px;
@@ -16,6 +21,10 @@
width: 20%;
}
+ th.orderBy.active {
+ font-weight:bold;
+ }
+
.title {
word-break: break-all;
overflow: hidden;
View
8 plugins/Insights/templates/insightControls.twig
@@ -1,4 +1,4 @@
-<div style="padding: 10px;padding-bottom: 0px;">
+<div class="controls">
{{ 'Insights_ControlGrowthDescription'|translate }}
@@ -14,7 +14,7 @@
{% endfor %}
</select>
- {% if period == 'day' or period == 'month' or period == 'week' %}
+ {% if period != 'range' %}
{{ 'Insights_ControlComparedToDescription'|translate }}
@@ -41,6 +41,8 @@
</select>
{% elseif period == 'week' %}
{{ 'Insights_WeekComparedToPreviousWeek'|translate }}
+ {% elseif period == 'year' %}
+ {{ 'Insights_YearComparedToPreviousYear'|translate }}
{% endif %}
{% endif %}
@@ -48,7 +50,7 @@
{{ 'Insights_Filter'|translate }}
- <select size="1" name="filterBy" title="Show all, only movers, only new, only disappeared">
+ <select size="1" name="filterBy" title="{{ 'Insights_ControlFilterByDescription'|translate|e('html_attr') }}">
<option {% if not properties.filter_by %}selected{% endif %} value="">
{{ 'General_All'|translate }}
</option>
View
15 plugins/Insights/templates/overviewWidget.twig
@@ -3,13 +3,12 @@
data-table-onlyinsightsinit="1">
{% if reports.getColumns|length > 0 %}
- {% for dataTable in reports.getDataTables() if dataTable.getRowsCount > 0 %}
- {% set metadata = dataTable.getAllTableMetadata %}
+ <table class="dataTable"
+ title="{{ consideredGrowth|e('html_attr') }} {{ consideredChanges|e('html_attr') }}">
+ {% for dataTable in reports.getDataTables() if dataTable.getRowsCount > 0 %}
+ {% set metadata = dataTable.getAllTableMetadata %}
- <table class="dataTable"
- title="{{ consideredGrowth|e('html_attr') }} {{ consideredChanges|e('html_attr') }}">
-
- <thead >
+ <thead>
{% include "@Insights/table_header.twig" %}
</thead>
@@ -19,8 +18,8 @@
{% endfor %}
</tbody>
- </table>
- {% endfor %}
+ {% endfor %}
+ </table>
<script type="text/javascript" defer="defer">
$(document).ready(function () {
View
8 plugins/Insights/templates/table_header.twig
@@ -2,12 +2,12 @@
<th class="label">
{{ metadata.reportName }}
</th>
- <th class="label orderBy" name="orderBy" value="absolute"
- style="{% if 'absolute' == properties.order_by %}font-weight:bold;{% endif %}">
+ <th class="label orderBy {% if 'absolute' == properties.order_by %}active{% endif %}"
+ name="orderBy" value="absolute">
{{ metadata.metricName }}
</th>
- <th class="label orderBy" name="orderBy" value="relative"
- style="{% if 'relative' == properties.order_by %}font-weight:bold;{% endif %}">
+ <th class="label orderBy {% if 'relative' == properties.order_by %}active{% endif %}"
+ name="orderBy" value="relative">
{{ 'MultiSites_Evolution'|translate }}
</th>
</tr>
View
3  plugins/Insights/tests/ApiTest.php
@@ -70,6 +70,9 @@ public function test_getInsights_ShouldReturnCorrectMetadata()
'minMoversPercent' => 2,
'minNewPercent' => 2,
'minDisappearedPercent' => 2,
+ 'lastTotalValue' => 59,
+ 'evolutionTotal' => -15.3,
+ 'evolutionDifference' => -9
);
$this->assertInternalType('array', $metadata['report']);
View
11 plugins/Referrers/Referrers.php
@@ -40,11 +40,20 @@ public function getListHooksRegistered()
'API.getReportMetadata' => 'getReportMetadata',
'API.getSegmentDimensionMetadata' => 'getSegmentsMetadata',
'ViewDataTable.configure' => 'configureViewDataTable',
- 'ViewDataTable.getDefaultType' => 'getDefaultTypeViewDataTable'
+ 'ViewDataTable.getDefaultType' => 'getDefaultTypeViewDataTable',
+ 'Insights.addReportToOverview' => 'addReportToInsightsOverview'
);
return $hooks;
}
+ public function addReportToInsightsOverview(&$reports)
+ {
+ $reports['Referrers_getWebsites'] = array();
+ $reports['Referrers_getCampaigns'] = array();
+ $reports['Referrers_getSocials'] = array();
+ $reports['Referrers_getSearchEngines'] = array();
+ }
+
public function getReportMetadata(&$reports)
{
$reports = array_merge($reports, array(
View
8 plugins/UserCountry/UserCountry.php
@@ -51,11 +51,17 @@ public function getListHooksRegistered()
'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
'ViewDataTable.configure' => 'configureViewDataTable',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
- 'Tracker.setTrackerCacheGeneral' => 'setTrackerCacheGeneral'
+ 'Tracker.setTrackerCacheGeneral' => 'setTrackerCacheGeneral',
+ 'Insights.addReportToOverview' => 'addReportToInsightsOverview'
);
return $hooks;
}
+ public function addReportToInsightsOverview(&$reports)
+ {
+ $reports['UserCountry_getCountry'] = array();
+ }
+
public function setTrackerCacheGeneral(&$cache)
{
$cache['currentLocationProviderId'] = LocationProvider::getCurrentProviderId();
Please sign in to comment.
Something went wrong with that request. Please try again.