/** * 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; }
/** 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); }
/** * 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; }
/** * 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; } }