/** * This method can be used by subclasses to iterate over data tables that might be * data table arrays. It calls back the template method self::doManipulate for each table. * This way, data table arrays can be handled in a transparent fashion. * * @param Piwik_DataTable_Array|Piwik_DataTable $dataTable * @throws Exception * @return Piwik_DataTable_Array|Piwik_DataTable */ protected function manipulate($dataTable) { if ($dataTable instanceof Piwik_DataTable_Array) { $newTableArray = new Piwik_DataTable_Array(); $newTableArray->metadata = $dataTable->metadata; $newTableArray->setKeyName($dataTable->getKeyName()); foreach ($dataTable->getArray() as $date => $subTable) { // for period=week, the label is "2011-08-15 to 2011-08-21", which is // an invalid date parameter => only use the first date // in other languages the whole string does not start with the date so // we need to preg match it. if (!preg_match('/[0-9]{4}(-[0-9]{2})?(-[0-9]{2})?/', $date, $match)) { throw new Exception("Could not recognize date: {$date}"); } $dateForApiRequest = $match[0]; $subTable = $this->doManipulate($subTable, $dateForApiRequest); $newTableArray->addTable($subTable, $date); } return $newTableArray; } if ($dataTable instanceof Piwik_DataTable) { return $this->doManipulate($dataTable); } return $dataTable; }
private function createInstanceWithDataTableArrays() { $dataTable = new Piwik_DataTable_Array(); $subDataTableArray1 = $this->createInstanceWithDataTables(); $subDataTableArray1->metadata['metadataKey1'] = 'metadataValue1'; $dataTable->addTable($subDataTableArray1, 'subArray1'); $subDataTableArray2 = $this->createInstanceWithDataTables(); $dataTable->addTable($subDataTableArray2, 'subArray2'); return $dataTable; }
/** * Computes the output of the given array of data tables * * @param Piwik_DataTable_Array $tableArray data tables to render * @param string $prefix prefix to output before table data * @return string */ protected function renderDataTableArray(Piwik_DataTable_Array $tableArray, $prefix) { $output = "Piwik_DataTable_Array<hr />"; $prefix = $prefix . ' '; foreach ($tableArray->getArray() as $descTable => $table) { $output .= $prefix . "<b>" . $descTable . "</b><br />"; $output .= $prefix . $this->renderTable($table, $prefix . ' '); $output .= "<hr />"; } $output .= "Metadata<br />"; foreach ($tableArray->metadata as $id => $metadata) { $output .= "<br />"; $output .= $prefix . " <b>{$id}</b><br />"; foreach ($metadata as $name => $value) { $output .= $prefix . $prefix . "{$name} => {$value}"; } } $output .= "<hr />"; return $output; }
/** Prepare metrics toggles with spark lines */ protected function getMetricsToggles($controller) { // calculate meta-metrics $subDataTables = $this->dataTable->getArray(); $firstDataTable = current($subDataTables); $firstDataTableRow = $firstDataTable->getFirstRow(); $lastDataTable = end($subDataTables); $lastDataTableRow = $lastDataTable->getFirstRow(); $maxValues = array(); $minValues = array(); foreach ($subDataTables as $subDataTable) { // $subDataTable is the report for one period, it has only one row $firstRow = $subDataTable->getFirstRow(); foreach ($this->availableMetrics as $metric => $label) { $value = $firstRow ? floatval($firstRow->getColumn($metric)) : 0; if (!isset($minValues[$metric]) || $minValues[$metric] > $value) { $minValues[$metric] = $value; } if (!isset($maxValues[$metric]) || $maxValues[$metric] < $value) { $maxValues[$metric] = $value; } } } $chart = new Piwik_Visualization_Chart_Evolution(); $colors = $chart->getSeriesColors(); // put together metric info $i = 0; $metrics = array(); foreach ($this->availableMetrics as $metric => $label) { if ($maxValues[$metric] == 0 && !$this instanceof Piwik_CoreHome_DataTableAction_MultiRowEvolution) { // series with only 0 cause trouble in js continue; } $first = $firstDataTableRow ? floatval($firstDataTableRow->getColumn($metric)) : 0; $last = $lastDataTableRow ? floatval($lastDataTableRow->getColumn($metric)) : 0; $changePercent = $first > 0 ? round($last / $first * 100 - 100) : 100; $changePercentHtml = $changePercent . '%'; if ($changePercent > 0) { $changePercentHtml = '+' . $changePercentHtml; $changeClass = 'up'; $changeImage = 'arrow_up'; } else { $changeClass = $changePercent < 0 ? 'down' : 'nochange'; $changeImage = $changePercent < 0 ? 'arrow_down' : false; } $changePercentHtml = '<span class="' . $changeClass . '">' . ($changeImage ? '<img src="plugins/MultiSites/images/' . $changeImage . '.png" /> ' : '') . $changePercentHtml . '</span>'; $details = Piwik_Translate('RowEvolution_MetricDetailsText', array($minValues[$metric], $maxValues[$metric], $changePercentHtml)); $color = $colors[$i % count($colors)]; $metrics[] = array('label' => $label, 'color' => $color, 'details' => $details, 'sparkline' => $this->getSparkline($metric, $controller)); $i++; } return $metrics; }
/** * This method searches for a recursive label. * The label parts are used to descend recursively until a complete match is found. * * The method will return a table containing only the matching row * or an empty data table. */ private function filterRecursive($labelParts, $dataTable) { if ($dataTable instanceof Piwik_DataTable_Array) { // search an array of tables, e.g. when using date=last30 // note that if the root is an array, we filter all children // if an array occurs inside the nested table, we only look for the first match (see below) $newTableArray = new Piwik_DataTable_Array(); $newTableArray->metadata = $dataTable->metadata; foreach ($dataTable->getArray() as $date => $subTable) { // for period=week, the label is "2011-08-15 to 2011-08-21", which is // an invalid date parameter => only use the first date (first 10 characters) $dateForApiRequest = substr($date, 0, 10); $subTable = $this->doFilterRecursive($labelParts, $subTable, $dateForApiRequest); $newTableArray->addTable($subTable, $date); } return $newTableArray; } return $this->doFilterRecursive($labelParts, $dataTable); }
/** Get row evolution for a multiple labels */ private function getMultiRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $column, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true) { $actualLabels = $logos = array(); $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal); if (!isset($metadata['metrics'][$column])) { // invalid column => use the first one that's available $metrics = array_keys($metadata['metrics']); $column = reset($metrics); } // load the tables for each label $dataTablesPerLabel = array(); $dataTableMetadata = false; foreach ($labels as $labelIndex => $label) { $dataTable = $this->loadRowEvolutionDataFromAPI($idSite, $period, $date, $apiModule, $apiAction, $label, $segment, $idGoal); $dataTablesPerLabel[$labelIndex] = $dataTable->getArray(); if (!$dataTableMetadata) { $dataTableMetadata = $dataTable->metadata; } $urlFound = false; foreach ($dataTablesPerLabel[$labelIndex] as $table) { if ($table->getRowsCount() > 0) { $firstRow = $table->getFirstRow(); // in case labels were replaced in the data table (e.g. for browsers report), // display the label from the table, not the one passed as filter $columnLabel = $firstRow->getColumn('label'); if (!empty($columnLabel)) { $actualLabels[$labelIndex] = $columnLabel; } list($actualLabel, $urlFound) = $this->cleanUrlForLabel($firstRow, $apiModule, $apiAction, $labelUseAbsoluteUrl); if ($actualLabel) { $actualLabels[$labelIndex] = $actualLabel; } // Forward the logo path to display logos in multi rows comparison $logos[$labelIndex] = $firstRow->getMetadata('logo'); break; } } if (!$urlFound) { $actualLabels[$labelIndex] = str_replace(Piwik_API_DataTableManipulator_LabelFilter::SEPARATOR_RECURSIVE_LABEL, ' - ', $label); } } // combine the tables $dataTableMulti = new Piwik_DataTable_Array(); $dataTableMulti->setKeyName($dataTable->getKeyName()); $dataTableMulti->metadata = $dataTableMetadata; foreach (array_keys(reset($dataTablesPerLabel)) as $dateLabel) { $newRow = new Piwik_DataTable_Row(); foreach ($dataTablesPerLabel as $labelIndex => $tableArray) { $table = $tableArray[$dateLabel]; if ($table->getRowsCount() == 0) { $value = 0; } else { $value = $table->getFirstRow()->getColumn($column); $value = floatVal(str_replace(',', '.', $value)); if ($value == '') { $value = 0; } } // keep metric in the label so that unit (%, s, ...) can be guessed correctly $label = $column . '_' . $labelIndex; $newRow->addColumn($label, $value); } $newTable = new Piwik_DataTable(); $newTable->addRow($newRow); $dataTableMulti->addTable($newTable, $dateLabel); } // the available metrics for the report are returned as metadata / columns $metadata['columns'] = $metadata['metrics']; // metadata / metrics should document the rows that are compared // this way, UI code can be reused $metadata['metrics'] = array(); foreach ($actualLabels as $labelIndex => $label) { if ($legendAppendMetric) { $label .= ' (' . $metadata['columns'][$column] . ')'; } $metricName = $column . '_' . $labelIndex; $metadata['metrics'][$metricName] = Piwik_DataTable_Filter_SafeDecodeLabel::safeDecodeLabel($label); if (!empty($logos[$labelIndex])) { $metadata['logos'][$metricName] = $logos[$labelIndex]; } } $this->enhanceRowEvolutionMetaData($metadata, $dataTableMulti); return array('column' => $column, 'reportData' => $dataTableMulti, 'metadata' => $metadata); }
/** * Returns a newly created Piwik_DataTable_Array. * * @return Piwik_DataTable_Array */ protected function getNewDataTableArray() { $table = new Piwik_DataTable_Array(); $table->setKeyName($this->getIndexName()); return $table; }
/** * Enhance a $dataTable using metadata : * * - remove metrics based on $reportMetadata['metrics'] * - add 0 valued metrics if $dataTable doesn't provide all $reportMetadata['metrics'] * - format metric values to a 'human readable' format * - extract row metadata to a separate Piwik_DataTable_Simple|Piwik_DataTable_Array : $rowsMetadata * - translate metric names to a separate array : $columns * * @param int $idSite enables monetary value formatting based on site currency * @param Piwik_DataTable|Piwik_DataTable_Array $dataTable * @param array $reportMetadata * @param boolean $hasDimension * @return array Piwik_DataTable_Simple|Piwik_DataTable_Array $newReport with human readable format & array $columns list of translated column names & Piwik_DataTable_Simple|Piwik_DataTable_Array $rowsMetadata **/ private function handleTableReport($idSite, $dataTable, &$reportMetadata, $hasDimension) { $columns = $reportMetadata['metrics']; if ($hasDimension) { $columns = array_merge(array('label' => $reportMetadata['dimension']), $columns); if (isset($reportMetadata['processedMetrics'])) { $processedMetricsAdded = $this->getDefaultProcessedMetrics(); foreach ($processedMetricsAdded as $processedMetricId => $processedMetricTranslation) { // this processed metric can be displayed for this report if (isset($reportMetadata['processedMetrics'][$processedMetricId])) { $columns[$processedMetricId] = $processedMetricTranslation; } } } // Display the global Goal metrics if (isset($reportMetadata['metricsGoal'])) { $metricsGoalDisplay = array('revenue'); // Add processed metrics to be displayed for this report foreach ($metricsGoalDisplay as $goalMetricId) { if (isset($reportMetadata['metricsGoal'][$goalMetricId])) { $columns[$goalMetricId] = $reportMetadata['metricsGoal'][$goalMetricId]; } } } if (isset($reportMetadata['processedMetrics'])) { // Add processed metrics $dataTable->filter('AddColumnsProcessedMetrics', array($deleteRowsWithNoVisit = false)); } } // $dataTable is an instance of Piwik_DataTable_Array when multiple periods requested if ($dataTable instanceof Piwik_DataTable_Array) { // Need a new Piwik_DataTable_Array to store the 'human readable' values $newReport = new Piwik_DataTable_Array(); $newReport->setKeyName("prettyDate"); $dataTableMetadata = $dataTable->metadata; $newReport->metadata = $dataTableMetadata; // Need a new Piwik_DataTable_Array to store report metadata $rowsMetadata = new Piwik_DataTable_Array(); $rowsMetadata->setKeyName("prettyDate"); // Process each Piwik_DataTable_Simple entry foreach ($dataTable->getArray() as $label => $simpleDataTable) { list($enhancedSimpleDataTable, $rowMetadata) = $this->handleSimpleDataTable($idSite, $simpleDataTable, $columns, $hasDimension); $period = $dataTableMetadata[$label]['period']->getLocalizedLongString(); $newReport->addTable($enhancedSimpleDataTable, $period); $rowsMetadata->addTable($rowMetadata, $period); } } else { list($newReport, $rowsMetadata) = $this->handleSimpleDataTable($idSite, $dataTable, $columns, $hasDimension); } return array($newReport, $columns, $rowsMetadata); }
/** * Computes the output of the given data table array * * @param Piwik_DataTable_Array $table * @param array $allColumns * @return string */ protected function renderDataTableArray($table, &$allColumns = array()) { $str = ''; foreach ($table->getArray() as $currentLinePrefix => $dataTable) { $returned = explode("\n", $this->renderTable($dataTable, $allColumns)); // get rid of the columns names $returned = array_slice($returned, 1); // case empty datatable we dont print anything in the CSV export // when in xml we would output <result date="2008-01-15" /> if (!empty($returned)) { foreach ($returned as &$row) { $row = $currentLinePrefix . $this->separator . $row; } $str .= "\n" . implode("\n", $returned); } } // prepend table key to column list $allColumns = array_merge(array($table->getKeyName() => true), $allColumns); // add header to output string $str = $this->getHeaderLine(array_keys($allColumns)) . $str; return $str; }
/** * Performs a binary filter of two * DataTables in order to correctly calculate evolution metrics. * * @param Piwik_DataTable|Piwik_DataTable_Array $currentData * @param Piwik_DataTable|Piwik_DataTable_Array $pastData * @param array $fields The array of string fields to calculate evolution * metrics for. */ private function calculateEvolutionPercentages($currentData, $pastData, $apiMetrics) { if ($currentData instanceof Piwik_DataTable_Array) { $pastArray = $pastData->getArray(); foreach ($currentData->getArray() as $subTable) { $this->calculateEvolutionPercentages($subTable, current($pastArray), $apiMetrics); next($pastArray); } } else { foreach ($apiMetrics as $metricSettings) { $currentData->filter('Piwik_MultiSites_CalculateEvolutionFilter', array($pastData, $metricSettings[self::METRIC_EVOLUTION_COL_NAME_KEY], $metricSettings[self::METRIC_RECORD_NAME_KEY], $quotientPrecision = 2)); } } }
/** * Returns a new DataTable_Array w/o any child DataTables, but with * the same key name as this instance. * * @return Piwik_DataTable_Array */ public function getEmptyClone() { $newTableArray = new Piwik_DataTable_Array(); $newTableArray->setKeyName($this->getKeyName()); return $newTableArray; }
/** * Given a Piwik_DataTable_Array made of DataTable_Simple rows, returns a php array with the structure: * array( * array( label => X, value => Y), * array( label => A, value => B), * ... * ) * * This is used for example for the evolution graph (last 30 days visits) or the sparklines. * * @param Piwik_DataTable_Array $dataTableArray * @return array */ protected function generateDataFromDataTableArray(Piwik_DataTable_Array $dataTableArray) { // we have to fill a $data array with each row = array('label' => X, 'value' => y) $data = array(); foreach ($dataTableArray->getArray() as $keyName => $table) { $value = false; $onlyRow = $table->getFirstRow(); if ($onlyRow !== false) { $value = $onlyRow->getColumn('value'); if ($value == false) { // TEMP // quite a hack, useful in the case at this point we do have a normal row with nb_visits, nb_actions, nb_uniq_visitors, etc. // instead of the dataTable_Simple row (label, value) // to do it properly we'd need to // - create a filter that removes columns // - apply this filter to keep only the column called nb_uniq_visitors // - rename this column as 'value' // and at this point the getcolumn('value') would have worked // this code is executed eg. when displaying a sparkline for the last 30 days displaying the number of unique visitors coming from search engines //TODO solution: use a filter rename column etc. // another solution would be to add a method to the Referers API giving directly the integer 'visits from search engines' // and we would build automatically the dataTable_array of datatatble_simple from these integers // but we'd have to add this integer to be recorded during archiving etc. $value = $onlyRow->getColumn('nb_uniq_visitors'); } } if ($value === false) { $value = 0; } $data[] = array('label' => $keyName, 'value' => $value); } return $data; }
/** * Computes the output for the given data table array * * @param Piwik_DataTable_Array $table * @param array $array * @param string $prefixLines * @return string */ protected function renderDataTableArray($table, $array, $prefixLines = "") { // CASE 1 //array // 'day1' => string '14' (length=2) // 'day2' => string '6' (length=1) $firstTable = current($array); if (!is_array($firstTable)) { $xml = ''; $nameDescriptionAttribute = $table->getKeyName(); foreach ($array as $valueAttribute => $value) { if (empty($value)) { $xml .= $prefixLines . "\t<result {$nameDescriptionAttribute}=\"{$valueAttribute}\" />\n"; } elseif ($value instanceof Piwik_DataTable_Array) { $out = $this->renderTable($value, true); //TODO somehow this code is not tested, cover this case $xml .= "\t<result {$nameDescriptionAttribute}=\"{$valueAttribute}\">\n{$out}</result>\n"; } else { $xml .= $prefixLines . "\t<result {$nameDescriptionAttribute}=\"{$valueAttribute}\">" . self::formatValueXml($value) . "</result>\n"; } } return $xml; } $subTables = $table->getArray(); $firstTable = current($subTables); // CASE 2 //array // 'day1' => // array // 'nb_uniq_visitors' => string '18' // 'nb_visits' => string '101' // 'day2' => // array // 'nb_uniq_visitors' => string '28' // 'nb_visits' => string '11' if ($firstTable instanceof Piwik_DataTable_Simple) { $xml = ''; $nameDescriptionAttribute = $table->getKeyName(); foreach ($array as $valueAttribute => $dataTableSimple) { if (count($dataTableSimple) == 0) { $xml .= $prefixLines . "\t<result {$nameDescriptionAttribute}=\"{$valueAttribute}\" />\n"; } else { if (is_array($dataTableSimple)) { $dataTableSimple = "\n" . $this->renderDataTableSimple($dataTableSimple, $prefixLines . "\t") . $prefixLines . "\t"; } $xml .= $prefixLines . "\t<result {$nameDescriptionAttribute}=\"{$valueAttribute}\">" . $dataTableSimple . "</result>\n"; } } return $xml; } // CASE 3 //array // 'day1' => // array // 0 => // array // 'label' => string 'phpmyvisites' // 'nb_uniq_visitors' => int 11 // 'nb_visits' => int 13 // 1 => // array // 'label' => string 'phpmyvisits' // 'nb_uniq_visitors' => int 2 // 'nb_visits' => int 2 // 'day2' => // array // 0 => // array // 'label' => string 'piwik' // 'nb_uniq_visitors' => int 121 // 'nb_visits' => int 130 // 1 => // array // 'label' => string 'piwik bis' // 'nb_uniq_visitors' => int 20 // 'nb_visits' => int 120 if ($firstTable instanceof Piwik_DataTable) { $xml = ''; $nameDescriptionAttribute = $table->getKeyName(); foreach ($array as $keyName => $arrayForSingleDate) { $dataTableOut = $this->renderDataTable($arrayForSingleDate, $prefixLines . "\t"); if (empty($dataTableOut)) { $xml .= $prefixLines . "\t<result {$nameDescriptionAttribute}=\"{$keyName}\" />\n"; } else { $xml .= $prefixLines . "\t<result {$nameDescriptionAttribute}=\"{$keyName}\">\n"; $xml .= $dataTableOut; $xml .= $prefixLines . "\t</result>\n"; } } return $xml; } if ($firstTable instanceof Piwik_DataTable_Array) { $xml = ''; $tables = $table->getArray(); $nameDescriptionAttribute = $table->getKeyName(); foreach ($tables as $valueAttribute => $tableInArray) { $out = $this->renderTable($tableInArray, true, $prefixLines . "\t"); $xml .= $prefixLines . "\t<result {$nameDescriptionAttribute}=\"{$valueAttribute}\">\n" . $out . $prefixLines . "\t</result>\n"; } return $xml; } }
/** * Utility function used by getAll. Performs a binary filter of two * DataTables in order to correctly calculate evolution metrics. * * @param Piwik_DataTable|Piwik_DataTable_Array $currentData * @param Piwik_DataTable|Piwik_DataTable_Array $pastData * @param array $fields The array of string fields to calculate evolution * metrics for. */ private function calculateEvolutionPercentages($currentData, $pastData, $fields) { if ($currentData instanceof Piwik_DataTable_Array) { $pastArray = $pastData->getArray(); foreach ($currentData->getArray() as $label => $subTable) { $this->calculateEvolutionPercentages($subTable, current($pastArray), $fields); next($pastArray); } } else { foreach ($fields as $field) { $currentData->filter('Piwik_MultiSites_CalculateEvolutionFilter', array($pastData, $this->evolutionColumnNames[$field], $field, $quotientPrecision = 2)); } } }
/** * Will search in the DataTable for a Label matching the searched string * and return only the matching row, or an empty datatable */ protected function getFilterPageDatatableSearch($callBackParameters, $search, $actionType, $table = false, $searchTree = false) { if ($searchTree === false) { // build the query parts that are searched inside the tree if ($actionType == Piwik_Tracker_Action::TYPE_ACTION_NAME) { $searchedString = Piwik_Common::unsanitizeInputValue($search); } else { $idSite = $callBackParameters[1]; try { $searchedString = Piwik_Tracker_Action::excludeQueryParametersFromUrl($search, $idSite); } catch (Exception $e) { $searchedString = $search; } } $searchTree = Piwik_Actions::getActionExplodedNames($searchedString, $actionType); } if ($table === false) { // fetch the data table $table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters); if ($table instanceof Piwik_DataTable_Array) { // search an array of tables, e.g. when using date=last30 // note that if the root is an array, we filter all children // if an array occurs inside the nested table, we only look for the first match (see below) $newTableArray = new Piwik_DataTable_Array(); $newTableArray->metadata = $table->metadata; $newTableArray->setKeyName($table->getKeyName()); foreach ($table->getArray() as $label => $subTable) { $subTable = $this->doFilterPageDatatableSearch($callBackParameters, $subTable, $searchTree); $newTableArray->addTable($subTable, $label); } return $newTableArray; } } return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree); }
/** * Merges the rows of every child DataTable into a new DataTable and * returns it. This function will also set the label of the merged rows * to the label of the DataTable they were originally from. * * The result of this function is determined by the type of DataTable * this instance holds. If this DataTable_Array instance holds an array * of DataTables, this function will transform it from: * <code> * Label 0: * DataTable(row1) * Label 1: * DataTable(row2) * </code> * to: * <code> * DataTable(row1[label = 'Label 0'], row2[label = 'Label 1']) * </code> * * If this instance holds an array of DataTable_Arrays, this function will * transform it from: * <code> * Outer Label 0: // the outer DataTable_Array * Inner Label 0: // one of the inner DataTable_Arrays * DataTable(row1) * Inner Label 1: * DataTable(row2) * Outer Label 1: * Inner Label 0: * DataTable(row3) * Inner Label 1: * DataTable(row4) * </code> * to: * <code> * Inner Label 0: * DataTable(row1[label = 'Outer Label 0'], row3[label = 'Outer Label 1']) * Inner Label 1: * DataTable(row2[label = 'Outer Label 0'], row4[label = 'Outer Label 1']) * </code> * * In addition, if this instance holds an array of DataTable_Arrays, the * metadata of the first child is used as the metadata of the result. * * This function can be used, for example, to smoosh IndexedBySite archive * query results into one DataTable w/ different rows differentiated by site ID. * * @return Piwik_DataTable|Piwik_DataTable_Array */ public function mergeChildren() { $firstChild = reset($this->array); if ($firstChild instanceof Piwik_DataTable_Array) { $result = new Piwik_DataTable_Array(); $result->setKeyName($firstChild->getKeyName()); $result->metadata = $firstChild->metadata; foreach ($this->array as $label => $subTableArray) { foreach ($subTableArray->array as $innerLabel => $subTable) { if (!isset($result->array[$innerLabel])) { $result->addTable(new Piwik_DataTable(), $innerLabel); } $this->copyRowsAndSetLabel($result->array[$innerLabel], $subTable, $label); } } } else { $result = new Piwik_DataTable(); foreach ($this->array as $label => $subTable) { $this->copyRowsAndSetLabel($result, $subTable, $label); } } return $result; }
/** * Returns a newly created Piwik_DataTable_Array. * The future elements of this array should be indexed by their dates (we set the index name to 'date'). * * @return Piwik_DataTable_Array */ protected function getNewDataTableArray() { $table = new Piwik_DataTable_Array(); $table->setNameKey('date'); return $table; }
protected function _getDataTableArray_containsDataTableArray_simpleOneRow() { $table = new Piwik_DataTable_Array(); $table->setKeyName('parentArrayKey'); $table->addTable($this->_getDataTableSimpleOneRowArrayTest(), 'idSite'); return $table; }
/** * Returns a new DataTable_Array w/ child tables that have had their * subtables merged. * * @see Piwik_DataTable::mergeSubtables * * @return Piwik_DataTable_Array */ public function mergeSubtables() { $result = new Piwik_DataTable_Array(); foreach ($this->array as $label => $childTable) { $result->addTable($childTable->mergeSubtables(), $label); } return $result; }
/** Get row evolution for a multiple labels */ private function getMultiRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $column, $language = false) { $actualLabels = array(); $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $segment, $language); if (!isset($metadata['metrics'][$column])) { // invalid column => use the first one that's available $column = reset(array_keys($metadata['metrics'])); } // load the tables for each label $dataTablesPerLabel = array(); $dataTableMetadata = false; foreach ($labels as $labelIndex => $label) { $dataTable = $this->loadRowEvolutionData($idSite, $period, $date, $apiModule, $apiAction, $label, $segment); $dataTablesPerLabel[$labelIndex] = $dataTable->getArray(); if (!$dataTableMetadata) { $dataTableMetadata = $dataTable->metadata; } $urlFound = false; foreach ($dataTablesPerLabel[$labelIndex] as $table) { if ($table->getRowsCount() > 0) { $firstRow = $table->getFirstRow(); // in case labels were replaced in the data table (e.g. for browsers report), // display the label from the table, not the one passed as filter $columnLabel = $firstRow->getColumn('label'); if (!empty($columnLabel)) { $actualLabels[$labelIndex] = $columnLabel; // TODO: confirm we need this // special case: websites report //if ($apiAction == 'getWebsites') //{ // $actualLabels[$labelIndex] = html_entity_decode($actualLabels[$labelIndex]); //} } // if url is available as metadata, use it (only for actions reports) if ($url = $firstRow->getMetadata('url')) { $actualLabels[$labelIndex] = $url; $urlFound = true; } break; } } if (!$urlFound) { // if we have a recursive label and no url, use the path $actualLabels[$labelIndex] = str_replace(Piwik_API_DataTableLabelFilter::SEPARATOR_RECURSIVE_LABEL, ' - ', $label); } } // combine the tables $dataTable = new Piwik_DataTable_Array(); $dataTable->setKeyName($dataTable->getKeyName()); $dataTable->metadata = $dataTableMetadata; foreach (array_keys(reset($dataTablesPerLabel)) as $dateLabel) { $newRow = new Piwik_DataTable_Row(); foreach ($dataTablesPerLabel as $labelIndex => $tableArray) { $table = $tableArray[$dateLabel]; if ($table->getRowsCount() == 0) { $value = 0; } else { $value = $table->getFirstRow()->getColumn($column); $value = floatVal(str_replace(',', '.', $value)); if ($value == '') { $value = 0; } } // keep metric in the label so that unit (%, s, ...) can be guessed correctly $label = $column . '_' . $labelIndex; $newRow->addColumn($label, $value); } $newTable = new Piwik_DataTable(); $newTable->addRow($newRow); $dataTable->addTable($newTable, $dateLabel); } // the available metrics for the report are returned as metadata / availableColumns $metadata['availableColumns'] = $metadata['metrics']; // metadata / metrics should document the rows that are compared // this way, UI code can be reused $metadata['metrics'] = array(); foreach ($actualLabels as $labelIndex => $label) { $label .= ' (' . $metadata['availableColumns'][$column] . ')'; $metadata['metrics'][$column . '_' . $labelIndex] = $label; } $this->enhanceRowEvolutionMetaData($metadata, $dataTable); return array('column' => $column, 'data' => $dataTable, 'metadata' => $metadata); }
/** * Load the data tables from the API and combine them into a data table * that can be plotted */ protected function loadDataTable() { $metadata = false; $dataTableArrays = array(); // load the tables for each label foreach ($this->labels as $rowLabelIndex => $rowLabel) { // TODO this used to be: $_GET['label'] = $this->label = $rowLabel; // is the $_GET assignment obsolete after label filter refactorings? $this->label = $rowLabel; $table = $this->doLoadDataTable(); $dataTableArrays[$rowLabelIndex] = $table->getArray(); if (!$metadata) { $metadata = $table->metadata; } $urlFound = false; foreach ($dataTableArrays[$rowLabelIndex] as $table) { if ($table->getRowsCount() > 0) { $firstRow = $table->getFirstRow(); // in case labels were replaced in the data table (e.g. for browsers report), // display the label from the table, not the one passed as filter $label = $firstRow->getColumn('label'); if (!empty($label)) { $this->labels[$rowLabelIndex] = $label; // special case: websites report if ($this->apiMethod == 'Referers.getWebsites') { $this->labels[$rowLabelIndex] = html_entity_decode($this->labels[$rowLabelIndex]); $urlFound = true; } } // if url is available as metadata, use it (only for actions reports) if (substr($this->apiMethod, 0, 7) == 'Actions' && ($url = $firstRow->getMetadata('url'))) { $this->labels[$rowLabelIndex] = $url; $urlFound = true; } break; } } if (!$urlFound && strpos($rowLabel, Piwik_API_DataTableLabelFilter::RECURSIVE_LABEL_SEPARATOR) !== false) { // if we have a recursive label and no url, use the path $this->labels[$rowLabelIndex] = str_replace(Piwik_API_DataTableLabelFilter::RECURSIVE_LABEL_SEPARATOR, ' - ', $rowLabel); } } // combine the tables $dataTable = new Piwik_DataTable_Array(); $dataTable->metadata = $metadata; foreach (array_keys(reset($dataTableArrays)) as $dateLabel) { $newRow = new Piwik_DataTable_Row(); foreach ($dataTableArrays as $rowLabelIndex => $tableArray) { $table = $tableArray[$dateLabel]; if ($table->getRowsCount() == 0) { $value = 0; } else { $value = $table->getFirstRow()->getColumn($this->metric); $value = floatVal(str_replace(',', '.', $value)); if ($value == '') { $value = 0; } } // keep metric in the label so that unit (%, s, ...) can be guessed correctly $label = $this->metric . '_' . $rowLabelIndex; $newRow->addColumn($label, $value); } $newTable = new Piwik_DataTable(); $newTable->addRow($newRow); $dataTable->addTable($newTable, $dateLabel); } // available metrics for metrics picker $this->metricsForSelect = $this->availableMetrics; $this->availableMetrics = array(); foreach ($this->labels as $rowLabelIndex => $label) { // add metric name $label .= ' (' . $this->metricsForSelect[$this->metric] . ')'; $this->availableMetrics[$this->metric . '_' . $rowLabelIndex] = $label; } return $dataTable; }