Skip to content

Commit

Permalink
refs #1816 return ratio in API
Browse files Browse the repository at this point in the history
  • Loading branch information
tsteur committed Nov 19, 2013
1 parent 10dee6a commit 7c15116
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 20 deletions.
43 changes: 24 additions & 19 deletions core/API/DataTableManipulator.php
Expand Up @@ -128,26 +128,8 @@ protected function loadSubtable($dataTable, $row)
}
}

$class = Request::getClassNameAPI($this->apiModule);
$method = $this->getApiMethodForSubtable();

$this->manipulateSubtableRequest($request);
$request['serialize'] = 0;
$request['expanded'] = 0;

// don't want to run recursive filters on the subtables as they are loaded,
// otherwise the result will be empty in places (or everywhere). instead we
// run it on the flattened table.
unset($request['filter_pattern_recursive']);

$dataTable = Proxy::getInstance()->call($class, $method, $request);
$response = new ResponseBuilder($format = 'original', $request);
$dataTable = $response->getResponse($dataTable);
if (method_exists($dataTable, 'applyQueuedFilters')) {
$dataTable->applyQueuedFilters();
}

return $dataTable;
return $this->callApiAndReturnDataTable($this->apiModule, $method, $request);
}

/**
Expand Down Expand Up @@ -177,4 +159,27 @@ private function getApiMethodForSubtable()
}
return $this->apiMethodForSubtable;
}

protected function callApiAndReturnDataTable($apiModule, $method, $request)
{
$class = Request::getClassNameAPI($apiModule);

$this->manipulateSubtableRequest($request);
$request['serialize'] = 0;
$request['expanded'] = 0;

// don't want to run recursive filters on the subtables as they are loaded,
// otherwise the result will be empty in places (or everywhere). instead we
// run it on the flattened table.
unset($request['filter_pattern_recursive']);

$dataTable = Proxy::getInstance()->call($class, $method, $request);
$response = new ResponseBuilder($format = 'original', $request);
$dataTable = $response->getResponse($dataTable);
if (method_exists($dataTable, 'applyQueuedFilters')) {
$dataTable->applyQueuedFilters();
}

return $dataTable;
}
}
188 changes: 188 additions & 0 deletions core/API/DataTableManipulator/AddRatioColumn.php
@@ -0,0 +1,188 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
* @category Piwik
* @package Piwik
*/
namespace Piwik\API\DataTableManipulator;

use Piwik\API\DataTableManipulator;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\DataTable\Filter;
use Piwik\Piwik;
use Piwik\Metrics;
use Piwik\Plugins\API\API;

/**
* This class is responsible for adding ratio columns.
*
* @package Piwik
* @subpackage Piwik_API
*/
class AddRatioColumn extends DataTableManipulator
{
protected $roundPrecision = 2;
private $totalValues = array();

/**
* @param DataTable $table
* @return \Piwik\DataTable|\Piwik\DataTable\Map
*/
public function addColumns($table)
{
return $this->manipulate($table);
}

/**
* Adds ratio metrics if possible.
*
* @param DataTable $dataTable
* @return DataTable
*/
protected function manipulateDataTable($dataTable)
{
$metricsToCalculate = Metrics::getMetricIdsToProcessRatio();

$parentTable = $this->getFirstLevelDataTable();

foreach ($parentTable->getRows() as $row) {
foreach ($metricsToCalculate as $metricId) {
$this->addColumnValueToTotal($row, $metricId);
}
}

foreach ($dataTable->getRows() as $row) {
foreach ($metricsToCalculate as $metricId) {
$this->addRatioColumnIfNeeded($row, $metricId);
}
}

return $dataTable;
}

protected function getFirstLevelDataTable()
{
$reportMetadata = API::getInstance()->getReportMetadata();

$firstLevelReport = array();
foreach ($reportMetadata as $report) {
if (!empty($report['actionToLoadSubTables'])
&& $this->apiMethod == $report['actionToLoadSubTables']
&& $this->apiModule == $report['module']) {

$firstLevelReport = $report;
break;
}
}

if (empty($firstLevelReport)) {
// it is not a subtable report
$module = $this->apiModule;
$action = $this->apiMethod;
} else {
$module = $firstLevelReport['module'];
$action = $firstLevelReport['action'];
}

$request = $this->request;

return $this->callApiAndReturnDataTable($module, $action, $request);
}

private function addColumnValueToTotal(Row $row, $columnIdRaw)
{
$value = $this->getColumn($row, $columnIdRaw);

if (false === $value) {

return;
}

if (array_key_exists($columnIdRaw, $this->totalValues)) {
$this->totalValues[$columnIdRaw] += $value;
} else {
$this->totalValues[$columnIdRaw] = $value;
}
}

private function addRatioColumnIfNeeded(Row $row, $columnIdRaw)
{
if (!array_key_exists($columnIdRaw, $this->totalValues)) {
return;
}

$value = $this->getColumn($row, $columnIdRaw);

if (false === $value) {
return;
}

$mappingIdToName = Metrics::$mappingFromIdToName;
$columnIdReadable = $mappingIdToName[$columnIdRaw];
$relativeValue = $this->getPercentage($value, $this->totalValues[$columnIdRaw]);

$row->addColumn($columnIdReadable . '_ratio_report', $relativeValue);
}

private function getPercentage($value, $totalValue)
{
$percentage = Piwik::getPercentageSafe($value, $totalValue, $this->roundPrecision);

return $percentage . '%';
}

/**
* Returns column from a given row.
* Will work with 2 types of datatable
* - raw datatables coming from the archive DB, which columns are int indexed
* - datatables processed resulting of API calls, which columns have human readable english names
*
* @param Row|array $row
* @param int $columnIdRaw see consts in Archive::
* @param bool|array $mappingIdToName
* @return mixed Value of column, false if not found
*/
protected function getColumn($row, $columnIdRaw, $mappingIdToName = false)
{
if (empty($mappingIdToName)) {
$mappingIdToName = Metrics::$mappingFromIdToName;
}

$columnIdReadable = $mappingIdToName[$columnIdRaw];

if ($row instanceof Row) {
$raw = $row->getColumn($columnIdRaw);
if ($raw !== false) {
return $raw;
}
return $row->getColumn($columnIdReadable);
}

return false;
}

/**
* Make sure to get all rows of the first level table.
*
* @param array $request
*/
protected function manipulateSubtableRequest(&$request)
{
$request['ratio'] = 0;
$request['filter_limit'] = -1;
$request['filter_offset'] = 0;

$parametersToRemove = array('flat', '');

foreach ($parametersToRemove as $param) {
if (array_key_exists($param, $request)) {
unset($request[$param]);
}
}
}
}
7 changes: 7 additions & 0 deletions core/API/ResponseBuilder.php
Expand Up @@ -11,6 +11,7 @@
namespace Piwik\API;

use Exception;
use Piwik\API\DataTableManipulator\AddRatioColumn;
use Piwik\API\DataTableManipulator\Flattener;
use Piwik\API\DataTableManipulator\LabelFilter;
use Piwik\Common;
Expand Down Expand Up @@ -299,6 +300,12 @@ protected function handleDataTable($datatable)
$datatable = $flattener->flatten($datatable);
}

// if the flag disable_generic_filters is defined we skip the generic filters
if (1 == Common::getRequestVar('ratio', '1', 'integer', $this->request)) {
$genericFilter = new AddRatioColumn($this->apiModule, $this->apiMethod, $this->request);
$datatable = $genericFilter->addColumns($datatable);
}

// if the flag disable_generic_filters is defined we skip the generic filters
if (0 == Common::getRequestVar('disable_generic_filters', '0', 'string', $this->request)) {
$genericFilter = new DataTableGenericFilter($this->request);
Expand Down
38 changes: 38 additions & 0 deletions core/Metrics.php
Expand Up @@ -286,6 +286,44 @@ static public function getDefaultProcessedMetrics()
return array_map(array('\\Piwik\\Piwik','translate'), $translations);
}

static public function getMetricIdsToProcessRatio()
{
return array(
Metrics::INDEX_NB_VISITS,
Metrics::INDEX_NB_UNIQ_VISITORS,
Metrics::INDEX_NB_ACTIONS,
Metrics::INDEX_PAGE_NB_HITS,
Metrics::INDEX_NB_VISITS_CONVERTED,
Metrics::INDEX_NB_CONVERSIONS
);
}

static public function getDefaultRatioMetrics()
{
$translations = array(
'nb_visits_ratio_report' => 'General_ColumnNbVisitsRatio',
'nb_uniq_visitors_ratio_report' => 'General_ColumnNbUniqVisitorsRatio',
'nb_actions_ratio_report' => 'General_ColumnNbActionsRatio',
'nb_hits_ratio_report' => 'General_ColumnNbHitsRatio',
'nb_visits_converted_ratio_report' => 'General_ColumnNbVisitsConvertedRatio'
);

return array_map(array('\\Piwik\\Piwik','translate'), $translations);
}

static public function getDefaultRatioMetricsDocumentation()
{
$translations = array(
'nb_visits_ratio_report' => 'General_ColumnNbVisitsRatioDocumentation',
'nb_uniq_visitors_ratio_report' => 'General_ColumnNbUniqVisitorsRatioDocumentation',
'nb_actions_ratio_report' => 'General_ColumnNbActionsRatioDocumentation',
'nb_hits_ratio_report' => 'General_ColumnNbHitsRatioDocumentation',
'nb_visits_converted_ratio_report' => 'General_ColumnNbConversionRatioDocumentation'
);

return array_map(array('\\Piwik\\Piwik','translate'), $translations);
}

static public function getDefaultMetricsDocumentation()
{
$documentation = array(
Expand Down
24 changes: 23 additions & 1 deletion plugins/API/ProcessedReport.php
Expand Up @@ -118,6 +118,10 @@ public function getReportMetadata($idSites, $period = false, $date = false, $hid
$availableReport['processedMetrics'] = Metrics::getDefaultProcessedMetrics();
}

if (!isset($availableReport['ratioMetrics'])) {
$availableReport['ratioMetrics'] = Metrics::getDefaultRatioMetrics();
}

if ($hideMetricsDoc) // remove metric documentation if it's not wanted
{
unset($availableReport['metricsDocumentation']);
Expand Down Expand Up @@ -154,7 +158,7 @@ public function getReportMetadata($idSites, $period = false, $date = false, $hid
// Add the magic API.get report metadata aggregating all plugins API.get API calls automatically
$this->addApiGetMetdata($availableReports);

$knownMetrics = array_merge(Metrics::getDefaultMetrics(), Metrics::getDefaultProcessedMetrics());
$knownMetrics = array_merge(Metrics::getDefaultMetrics(), Metrics::getDefaultProcessedMetrics(), Metrics::getDefaultRatioMetrics());
foreach ($availableReports as &$availableReport) {
// Ensure all metrics have a translation
$metrics = $availableReport['metrics'];
Expand All @@ -177,6 +181,9 @@ public function getReportMetadata($idSites, $period = false, $date = false, $hid
if (isset($availableReport['processedMetrics'])) {
$availableReport['processedMetrics'] = $this->hideShowMetrics($availableReport['processedMetrics']);
}
if (isset($availableReport['ratioMetrics'])) {
$availableReport['ratioMetrics'] = $this->hideShowMetrics($availableReport['ratioMetrics']);
}
if (isset($availableReport['metricsDocumentation'])) {
$availableReport['metricsDocumentation'] =
$this->hideShowMetrics($availableReport['metricsDocumentation']);
Expand Down Expand Up @@ -376,6 +383,21 @@ private function handleTableReport($idSite, $dataTable, &$reportMetadata, $showR
$columns
);

if (isset($reportMetadata['ratioMetrics'])) {
$ratioMetricDocs = Metrics::getDefaultRatioMetricsDocumentation();

// we automatically detect which ratio metrics should be added
foreach ($reportMetadata['ratioMetrics'] as $ratioMetricId => $ratioMetricTranslation) {
if (array_filter($dataTable->getColumn($ratioMetricId))) {
$columns[$ratioMetricId] = $ratioMetricTranslation;

if (array_key_exists('metricsDocumentation', $reportMetadata)) {
$reportMetadata['metricsDocumentation'][$ratioMetricId] = $ratioMetricDocs[$ratioMetricId];
}
}
}
}

if (isset($reportMetadata['processedMetrics'])) {
$processedMetricsAdded = Metrics::getDefaultProcessedMetrics();
foreach ($processedMetricsAdded as $processedMetricId => $processedMetricTranslation) {
Expand Down

0 comments on commit 7c15116

Please sign in to comment.