Skip to content

Commit

Permalink
refs #57 some more code tweaks and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
tsteur committed Mar 10, 2014
1 parent 03583a9 commit 3710bb4
Show file tree
Hide file tree
Showing 14 changed files with 223 additions and 46 deletions.
1 change: 1 addition & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,7 @@
"WeekComparedToPreviousWeek": "previous week",
"MonthComparedToPreviousMonth": "previous month",
"MonthComparedToPreviousYear": "same month in the previous year",
"YearComparedToPreviousYear": "previous year",
"Filter": "Filter",
"FilterOnlyMovers": "Only movers",
"FilterOnlyNew": "Only new",
Expand Down
10 changes: 9 additions & 1 deletion plugins/Actions/Actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
159 changes: 138 additions & 21 deletions plugins/Insights/API.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand All @@ -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();
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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;
}
}
}

Expand All @@ -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)
{
Expand All @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
3 changes: 3 additions & 0 deletions plugins/Insights/DataTable/Filter/ExcludeLowValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

use Piwik\DataTable;

/**
* Removes all rows whose value is too low.
*/
class ExcludeLowValue extends DataTable\BaseFilter
{
private $minimumValue;
Expand Down
20 changes: 12 additions & 8 deletions plugins/Insights/DataTable/Filter/Limit.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
}

Expand Down
21 changes: 13 additions & 8 deletions plugins/Insights/DataTable/Filter/MinGrowth.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,37 @@
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;
}

foreach ($table->getRows() as $key => $row) {

$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;
}

Expand Down
9 changes: 9 additions & 0 deletions plugins/Insights/DataTable/Filter/OrderBy.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions plugins/Insights/InsightReport.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 3710bb4

Please sign in to comment.