Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compute row percentages in PHP before metrics are formatted to avoid percentage formatting WARNINGS. #15304

Merged
merged 8 commits into from
Jan 3, 2020
110 changes: 86 additions & 24 deletions plugins/CoreVisualizations/Visualizations/HtmlTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\DataTable;
use Piwik\NumberFormatter;
use Piwik\Period;
use Piwik\Piwik;
use Piwik\Plugin\Visualization;

/**
Expand Down Expand Up @@ -81,31 +83,9 @@ public function beforeRender()
}
}

// we do not want to get a datatable\map
$period = Common::getRequestVar('period', 'day', 'string');
if (Period\Range::parseDateRange($period)) {
$period = 'range';
}

if ($this->dataTable->getRowsCount()) {
$request = new ApiRequest(array(
'method' => 'API.get',
'module' => 'API',
'action' => 'get',
'format' => 'original',
'filter_limit' => '-1',
'disable_generic_filters' => 1,
'expanded' => 0,
'flat' => 0,
'filter_offset' => 0,
'period' => $period,
'showColumns' => implode(',', $this->config->columns_to_display),
'columns' => implode(',', $this->config->columns_to_display),
'pivotBy' => ''
));

$dataTable = $request->process();
$this->assignTemplateVar('siteSummary', $dataTable);
$siteTotalRow = $this->getSiteSummary() ? $this->getSiteSummary()->getFirstRow() : null;
$this->assignTemplateVar('siteTotalRow', $siteTotalRow);
}

if ($this->isPivoted()) {
Expand Down Expand Up @@ -222,6 +202,57 @@ public function beforeGenericFiltersAreAppliedToLoadedDataTable()
}
}

public function afterGenericFiltersAreAppliedToLoadedDataTable()
{
parent::afterGenericFiltersAreAppliedToLoadedDataTable();

$this->calculateTotalPercentages(); // this must be done before metrics are formatted
}

private function calculateTotalPercentages()
{
if (empty($this->report)) {
return;
}

$columnNamesToIndices = Metrics::getMappingFromNameToId();
$formatter = NumberFormatter::getInstance();

$totals = $this->dataTable->getMetadata('totals');

$siteSummary = $this->getSiteSummary();
$siteTotalRow = $siteSummary ? $siteSummary->getFirstRow() : null;

foreach ($this->dataTable->getRows() as $row) {
foreach ($this->report->getMetrics() as $column => $translation) {
$indexColumn = $columnNamesToIndices[$column]; // TODO: This might fail for goal specific columns...

$value = $row->getColumn($indexColumn) ?: $row->getColumn($column);
if ($column == 'label'
|| $value === false
) {
continue;
}

$reportTotal = isset($totals[$column]) ? $totals[$column] : 0;

$percentageColumnName = $column . '_row_percentage';
$rowPercentage = $formatter->formatPercent(Piwik::getPercentageSafe($value, $reportTotal, $precision = 1));
$row->setMetadata($percentageColumnName, $rowPercentage);

if ($siteTotalRow) {
$siteTotal = $siteTotalRow->getColumn($column) ?: 0;

$siteTotalPercentage = $column . '_site_total_percentage';
if ($siteTotal && $siteTotal > $reportTotal) {
$rowPercentage = $formatter->formatPercent(Piwik::getPercentageSafe($value, $siteTotal, $precision = 1));
$row->setMetadata($siteTotalPercentage, $rowPercentage);
}
}
}
}
}

protected function isPivoted()
{
return $this->requestConfig->pivotBy || Common::getRequestVar('pivotBy', '');
Expand All @@ -248,4 +279,35 @@ protected function isFlattened()
{
return $this->requestConfig->flat || Common::getRequestVar('flat', '');
}

private function getSiteSummary()
{
if (empty($this->siteSummary)) {
// we do not want to get a datatable\map
$period = Common::getRequestVar('period', 'day', 'string');
if (Period\Range::parseDateRange($period)) {
$period = 'range';
}

$request = new ApiRequest(array(
'method' => 'API.get',
'module' => 'API',
'action' => 'get',
'format' => 'original',
'filter_limit' => '-1',
'disable_generic_filters' => 1,
'expanded' => 0,
'flat' => 0,
'filter_offset' => 0,
'period' => $period,
'showColumns' => implode(',', $this->config->columns_to_display),
'columns' => implode(',', $this->config->columns_to_display),
'pivotBy' => ''
));

$this->siteSummary = $request->process();
}

return $this->siteSummary;
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
{% if column in properties.report_ratio_columns and (column in totals|keys or forceZero|default) -%}
{% set reportTotal = totals[column]|default(0) %}
{% if siteTotalRow|default is not empty %}

{% if siteTotalRow is defined and siteTotalRow is not empty %}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@diosmosis do you understand why we calculate the percentage differently

  • The percentage value we actually show is based on reportTotal
  • The percentage value in tooltip is based on siteTotal

I wonder if we can't always use the same logic? Eg always use reportTotal? Then we wouldn't even need to do the API call in the HTML table maybe. I suppose the reportTotal may be formatted as well though. Actually I think I get it... the siteTotal is without segment while reportTotal may have a segment...

Looks good to merge @diosmosis

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I think I get it... the siteTotal is without segment while reportTotal may have a segment...

Interesting, I had no idea why this was there :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I just tested it and it does actually not seem to be the case. I reckon the siteSummary might not be needed at all and we can just use the same percentage as we show in the table based on reportTotal?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this is the commit that differentiated between totals & siteSummary: bb0e02d

Maybe siteSummary was supposed to get the data w/o segment, but because we weren't using Request::processRequest(..., $default = []) or something similar, it wasn't working as expected? I wonder if this is actually a bug and we can fix it by making siteSummary work the way you thought it worked.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds great to add the default 👍

{% set siteTotal = siteTotalRow.getColumn(column) %}
{% elseif siteSummary is defined and siteSummary is not empty and siteSummary.getFirstRow %}
{% set siteTotal = siteSummary.getFirstRow.getColumn(column) %}
{% else %}
{% set siteTotal = 0 %}
{% endif %}

{% set rowPercentage = row.getColumn(column)|percentage(reportTotal, 1) %}
{% set rowPercentage = row.getMetadata(column ~ '_row_percentage') %}
{% set metricTitle = translations[column]|default(column) %}

{% set reportRatioTooltip = 'General_ReportRatioTooltip'|translate(label, rowPercentage|e('html_attr'), reportTotal|e('html_attr'), metricTitle|e('html_attr'), '"' ~ segmentTitlePretty ~ '"', translations[labelColumn]|default(labelColumn)|e('html_attr')) %}

{% if siteTotal and siteTotal > reportTotal %}
{% set totalPercentage = row.getColumn(column)|percentage(siteTotal, 1) %}
{% if row.getMetadata(column ~ '_site_total_percentage') != false %}
{% set totalPercentage = row.getMetadata(column ~ '_site_total_percentage') %}
{% set totalRatioTooltip = 'General_TotalRatioTooltip'|translate(totalPercentage, siteTotal|number(2,0), metricTitle, periodTitlePretty) %}
{% else %}
{% set totalRatioTooltip = '' %}
Expand Down
2 changes: 0 additions & 2 deletions plugins/Goals/API.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
use Piwik\Cache as PiwikCache;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Db;
use Piwik\Metrics;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Plugins\API\DataTable\MergeDataTables;
use Piwik\Plugins\CoreHome\Columns\Metrics\ConversionRate;
use Piwik\Plugins\Goals\Columns\Metrics\AverageOrderRevenue;
Expand Down