Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Fabian Dellwing committed Mar 12, 2019
2 parents 5f5bf01 + 7edf461 commit 7ddf7c4
Show file tree
Hide file tree
Showing 119 changed files with 1,543 additions and 686 deletions.
1 change: 1 addition & 0 deletions core/Application/Kernel/PluginList.php
Expand Up @@ -39,6 +39,7 @@ class PluginList
'ExampleVisualization',
'ExamplePluginTemplate',
'ExampleTracker',
'ExampleLogTables',
'ExampleReport',
'MobileAppMeasurable',
'Provider',
Expand Down
46 changes: 46 additions & 0 deletions core/DataAccess/LogQueryBuilder/JoinGenerator.php
Expand Up @@ -53,6 +53,19 @@ private function addMissingTablesNeededForJoins()
if (!$logTable->getColumnToJoinOnIdVisit()) {
$tableNameToJoin = $logTable->getLinkTableToBeAbleToJoinOnVisit();

if (empty($tableNameToJoin) && $logTable->getWaysToJoinToOtherLogTables()) {
foreach ($logTable->getWaysToJoinToOtherLogTables() as $otherLogTable => $column) {
if ($this->tables->hasJoinedTable($otherLogTable)) {
$this->tables->addTableDependency($table, $otherLogTable);
continue;
}
if ($this->tables->isTableJoinableOnVisit($otherLogTable) || $this->tables->isTableJoinableOnAction($otherLogTable)) {
$this->addMissingTablesForOtherTableJoin($otherLogTable, $table);
}
}
continue;
}

if ($index > 0 && !$this->tables->hasJoinedTable($tableNameToJoin)) {
$this->tables->addTableToJoin($tableNameToJoin);
}
Expand Down Expand Up @@ -96,6 +109,30 @@ private function addMissingTablesNeededForJoins()
}
}

private function addMissingTablesForOtherTableJoin($tableName, $dependentTable)
{
$this->tables->addTableDependency($dependentTable, $tableName);

if ($this->tables->hasJoinedTable($tableName)) {
return;
}

$table = $this->tables->getLogTable($tableName);

if ($table->getColumnToJoinOnIdAction() || $table->getColumnToJoinOnIdAction() || $table->getLinkTableToBeAbleToJoinOnVisit()) {
$this->tables->addTableToJoin($tableName);
return;
}

$otherTableJoins = $table->getWaysToJoinToOtherLogTables();

foreach ($otherTableJoins as $logTable => $column) {
$this->addMissingTablesForOtherTableJoin($logTable, $tableName);
}

$this->tables->addTableToJoin($tableName);
}

/**
* Generate the join sql based on the needed tables
* @throws Exception if tables can't be joined
Expand Down Expand Up @@ -206,6 +243,15 @@ public function findJoinCriteriasForTables(LogTable $logTable, $availableLogTabl

break;
}

$otherJoins = $logTable->getWaysToJoinToOtherLogTables();
foreach ($otherJoins as $joinTable => $column) {
if($availableLogTable->getName() == $joinTable) {
$join = sprintf("`%s`.`%s` = `%s`.`%s`", $table, $column, $availableLogTable->getName(), $column);
break;
}
}

}

if (!isset($join)) {
Expand Down
135 changes: 102 additions & 33 deletions core/DataAccess/LogQueryBuilder/JoinTables.php
Expand Up @@ -19,6 +19,40 @@ class JoinTables extends \ArrayObject
*/
private $logTableProvider;

// NOTE: joins can be specified explicitly as arrays w/ 'joinOn' keys or implicitly as table names. when
// table names are used, the joins dependencies are assumed based on how we want to order those joins.
// the below table list the possible dependencies of each table, and is specifically designed to enforce
// the following order:
// log_link_visit_action, log_action, log_visit, log_conversion, log_conversion_item
// which means if an array is supplied where log_visit comes before log_link_visitAction, it will
// be moved to after it.
private $implicitTableDependencies = [
'log_link_visit_action' => [
// empty
],
'log_action' => [
'log_link_visit_action',
'log_conversion',
'log_conversion_item',
'log_visit',
],
'log_visit' => [
'log_link_visit_action',
'log_action',
],
'log_conversion' => [
'log_link_visit_action',
'log_action',
'log_visit',
],
'log_conversion_item' => [
'log_link_visit_action',
'log_action',
'log_visit',
'log_conversion',
],
];

/**
* Tables constructor.
* @param LogTablesProvider $logTablesProvider
Expand Down Expand Up @@ -125,6 +159,73 @@ public function sort()
$this->exchangeArray($sorted);
}

public function isTableJoinableOnVisit($tableToCheck)
{
$table = $this->getLogTable($tableToCheck);

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

if ($table->getColumnToJoinOnIdVisit()) {
return true;
}

if ($table->getLinkTableToBeAbleToJoinOnVisit()) {
return true;
}

$otherWays = $table->getWaysToJoinToOtherLogTables();

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

foreach ($otherWays as $logTable => $column) {
if ($logTable == 'log_visit' || $this->isTableJoinableOnVisit($logTable)) {
return true;
}
}

return false;
}

public function isTableJoinableOnAction($tableToCheck)
{
$table = $this->getLogTable($tableToCheck);

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

if ($table->getColumnToJoinOnIdAction()) {
return true;
}

$otherWays = $table->getWaysToJoinToOtherLogTables();

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

foreach ($otherWays as $logTable => $column) {
if ($logTable == 'log_action' || $this->isTableJoinableOnAction($logTable)) {
return true;
}
}

return false;
}

public function addTableDependency($table, $dependentTable)
{
if (!empty($this->implicitTableDependencies[$table])) {
return;
}

$this->implicitTableDependencies[$table] = [$dependentTable];
}

private function checkTableCanBeUsedForSegmentation($tableName)
{
if (!is_array($tableName) && !$this->getLogTable($tableName)) {
Expand Down Expand Up @@ -154,39 +255,7 @@ private function parseDependencies(array $tables)

private function assumeImplicitJoinDependencies($allTablesToQuery, $table)
{
// NOTE: joins can be specified explicitly as arrays w/ 'joinOn' keys or implicitly as table names. when
// table names are used, the joins dependencies are assumed based on how we want to order those joins.
// the below table list the possible dependencies of each table, and is specifically designed to enforce
// the following order:
// log_link_visit_action, log_action, log_visit, log_conversion, log_conversion_item
// which means if an array is supplied where log_visit comes before log_link_visitAction, it will
// be moved to after it.
$implicitTableDependencies = [
'log_link_visit_action' => [
// empty
],
'log_action' => [
'log_link_visit_action',
'log_conversion',
'log_conversion_item',
'log_visit',
],
'log_visit' => [
'log_link_visit_action',
'log_action',
],
'log_conversion' => [
'log_link_visit_action',
'log_action',
'log_visit',
],
'log_conversion_item' => [
'log_link_visit_action',
'log_action',
'log_visit',
'log_conversion',
],
];
$implicitTableDependencies = $this->implicitTableDependencies;

$result = [];
if (isset($implicitTableDependencies[$table])) {
Expand Down
22 changes: 21 additions & 1 deletion core/Visualization/Sparkline.php
Expand Up @@ -70,8 +70,28 @@ public function main() {
}
$values[] = $value;
}
$sparkline->setData($values);

$hasFloat = false;
foreach ($values as $value) {
if (is_numeric($value)
&& is_float($value + 0) // coerce to int/float type before checking
) {
$hasFloat = true;
break;
}
}

// the sparkline lib used converts everything to integers (see the FormatTrait.php file) which means float
// numbers that are close to 1.0 or 0.0 will get floored. this can happen in the average page generation time
// report, and cause some values which are, eg, around ~.9 to appear as 0 in the sparkline. to workaround this, we
// scale the values.
if ($hasFloat) {
$values = array_map(function ($x) {
return $x * 1000.0;
}, $values);
}

$sparkline->setData($values);
$sparkline->setWidth($this->getWidth());
$sparkline->setHeight($this->getHeight());
$this->setSparklineColors($sparkline);
Expand Down
18 changes: 9 additions & 9 deletions plugins/Actions/API.php
Expand Up @@ -95,7 +95,7 @@ public function getPageUrls($idSite, $period, $date, $segment = false, $expanded

$dataTable = Archive::createDataTableFromArchive('Actions_actions_url', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable, $depth);

$this->filterActionsDataTable($dataTable, $isPageTitleType = false);
$this->filterActionsDataTable($dataTable, Action::TYPE_PAGE_URL);

if ($flat) {
$dataTable->filter(function(DataTable $dataTable) {
Expand Down Expand Up @@ -196,7 +196,7 @@ public function getPageUrl($pageUrl, $idSite, $period, $date, $segment = false)
$callBackParameters = array('Actions_actions_url', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageUrl, Action::TYPE_PAGE_URL);
$this->addPageProcessedMetrics($dataTable);
$this->filterActionsDataTable($dataTable, $isPageTitleType = false);
$this->filterActionsDataTable($dataTable, Action::TYPE_PAGE_URL);
return $dataTable;
}

Expand All @@ -206,7 +206,7 @@ public function getPageTitles($idSite, $period, $date, $segment = false, $expand

$dataTable = Archive::createDataTableFromArchive('Actions_actions', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable);

$this->filterActionsDataTable($dataTable, $isPageTitleType = true);
$this->filterActionsDataTable($dataTable, Action::TYPE_PAGE_TITLE);

return $dataTable;
}
Expand Down Expand Up @@ -246,7 +246,7 @@ public function getPageTitle($pageName, $idSite, $period, $date, $segment = fals
$callBackParameters = array('Actions_actions', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageName, Action::TYPE_PAGE_TITLE);
$this->addPageProcessedMetrics($dataTable);
$this->filterActionsDataTable($dataTable, $isPageTitleType = true);
$this->filterActionsDataTable($dataTable, Action::TYPE_PAGE_TITLE);
return $dataTable;
}

Expand All @@ -255,7 +255,7 @@ public function getDownloads($idSite, $period, $date, $segment = false, $expande
Piwik::checkUserHasViewAccess($idSite);

$dataTable = Archive::createDataTableFromArchive('Actions_downloads', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable);
$this->filterActionsDataTable($dataTable, $isPageTitleType = false);
$this->filterActionsDataTable($dataTable, Action::TYPE_DOWNLOAD);
return $dataTable;
}

Expand All @@ -265,7 +265,7 @@ public function getDownload($downloadUrl, $idSite, $period, $date, $segment = fa

$callBackParameters = array('Actions_downloads', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $downloadUrl, Action::TYPE_DOWNLOAD);
$this->filterActionsDataTable($dataTable, $isPageTitleType = false);
$this->filterActionsDataTable($dataTable, Action::TYPE_DOWNLOAD);
return $dataTable;
}

Expand All @@ -274,7 +274,7 @@ public function getOutlinks($idSite, $period, $date, $segment = false, $expanded
Piwik::checkUserHasViewAccess($idSite);

$dataTable = Archive::createDataTableFromArchive('Actions_outlink', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable);
$this->filterActionsDataTable($dataTable, $isPageTitleType = false);
$this->filterActionsDataTable($dataTable, Action::TYPE_OUTLINK);
return $dataTable;
}

Expand All @@ -284,7 +284,7 @@ public function getOutlink($outlinkUrl, $idSite, $period, $date, $segment = fals

$callBackParameters = array('Actions_outlink', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $outlinkUrl, Action::TYPE_OUTLINK);
$this->filterActionsDataTable($dataTable, $isPageTitleType = false);
$this->filterActionsDataTable($dataTable, Action::TYPE_OUTLINK);
return $dataTable;
}

Expand All @@ -294,7 +294,7 @@ public function getSiteSearchKeywords($idSite, $period, $date, $segment = false)

$dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment);
$dataTable->deleteColumn(PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT);
$this->filterActionsDataTable($dataTable, $isPageTitleType = false);
$this->filterActionsDataTable($dataTable, Action::TYPE_SITE_SEARCH);
$dataTable->filter('ReplaceColumnNames');
$dataTable->filter('AddSegmentByLabel', array('siteSearchKeyword'));
$this->addPagesPerSearchColumn($dataTable);
Expand Down

0 comments on commit 7ddf7c4

Please sign in to comment.