diff --git a/application/controllers/admin/export.php b/application/controllers/admin/export.php index f6cb1814a8b..232e1977fcf 100644 --- a/application/controllers/admin/export.php +++ b/application/controllers/admin/export.php @@ -298,6 +298,11 @@ public function exportresults() $sFilter=''; } + // Now disable logging to prevent errors when for example database logging is on + foreach (App()->log->routes as $route) + { + $route->enabled = $route->enabled && !($route instanceOf CWebLogRoute); + } $resultsService->exportSurvey($iSurveyID, $explang, $type, $options, $sFilter); exit; @@ -608,276 +613,6 @@ public function exportspss() } } - /* - * The SPSS DATA LIST / BEGIN DATA parser is rather simple minded, the number after the type - * specifier identifies the field width (maximum number of characters to scan) - * It will stop short of that number of characters, honouring quote delimited - * space separated strings, however if the width is too small the remaining data in the current - * line becomes part of the next column. Since we want to restrict this script to ONE scan of - * the data (scan & output at same time), the information needed to construct the - * DATA LIST is held in the $fields array, while the actual data is written to a - * to a temporary location, updating length (size) values in the $fields array as - * the tmp file is generated (uses @fwrite's return value rather than strlen). - * Final output renders $fields to a DATA LIST, and then stitches in the tmp file data. - * - * Optimization opportunities remain in the VALUE LABELS section, which runs a query / column - */ - public function exportr() - { - global $length_vallabel; - $iSurveyID = sanitize_int(Yii::app()->request->getParam('sid')); - $subaction = Yii::app()->request->getParam('subaction'); - - $clang = $this->getController()->lang; - //for scale 1=nominal, 2=ordinal, 3=scale - - //$typeMap = $this->_getTypeMap(); - - $length_vallabel = '120'; // Set the max text length of Value Labels - $iLength = '25500'; // Set the max text length of Text Data - $length_varlabel = '25500'; // Set the max text length of Variable Labels - $headerComment = ''; -// $tempFile = ''; - - if ( ! isset($iSurveyID) ) { $iSurveyID = returnGlobal('sid'); } - $filterstate = incompleteAnsFilterState(); - - $headerComment = '#$Rev: 10193 $' . " $filterstate.\n"; - - if ( isset($_POST['dldata']) ) $subaction = "dldata"; - if ( isset($_POST['dlstructure']) ) $subaction = "dlstructure"; - - if ( ! isset($subaction) ) - { - $selecthide = ""; - $selectshow = ""; - $selectinc = ""; - - switch ( $filterstate ) - { - case "incomplete": - $selectinc = "selected='selected'"; - break; - case "complete": - $selecthide = "selected='selected'"; - break; - default: - $selectshow = "selected='selected'"; - } - - $data['selectinc'] = $selectinc; - $data['selecthide'] = $selecthide; - $data['selectshow'] = $selectshow; - $data['filename'] = "survey_" . $iSurveyID . "_R_syntax_file.R"; - $data['surveyid'] = $iSurveyID; - $data['display']['menu_bars']['browse'] = $clang->gT("Export results"); - - $this->_renderWrappedTemplate('export', 'r_view', $data); - } - else - { - Yii::app()->loadHelper("admin/exportresults"); - } - - - if ( $subaction == 'dldata' ) - { - header("Content-Disposition: attachment; filename=survey_" . $iSurveyID . "_R_data_file.csv"); - header("Content-type: text/comma-separated-values; charset=UTF-8"); - header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); - header("Pragma: public"); - - $na = ""; //change to empty string instead of two double quotes to fix warnings on NA - SPSSExportData($iSurveyID, $iLength, $na='', $q='"', $header=TRUE); - - exit; - } - - if ( $subaction == 'dlstructure' ) - { - header("Content-Disposition: attachment; filename=survey_" . $iSurveyID . "_R_syntax_file.R"); - header("Content-type: application/download; charset=UTF-8"); - header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); - header("Pragma: public"); - - echo $headerComment; - - echo ('data <- read.csv("survey_' . $iSurveyID .'_R_data_file.csv", quote = "\'\"", na.strings=c("", "\"\""), stringsAsFactors=FALSE)'); - echo ("\n\n"); - - // Build array that has to be returned - $fields = SPSSFieldMap($iSurveyID,"V"); - - //Now get the query string with all fields to export - $query = SPSSGetQuery($iSurveyID, 500, 0); // Sample first 500 responses for adjusting fieldmap - $result = $query->query(); - - $num_fields = 0; - //Now we check if we need to adjust the size of the field or the type of the field - foreach ( $result as $row ) - { - if($num_fields==0) { - $num_fields = count($row); - } - $row = array_values($row); - $fieldno = 0; - - while ( $fieldno < $num_fields ) - { - //Performance improvement, don't recheck fields that have valuelabels - if ( ! isset($fields[$fieldno]['answers']) ) - { - $strTmp = mb_substr(stripTagsFull($row[$fieldno]), 0, $iLength); - $len = mb_strlen($strTmp); - - if ( $len > $fields[$fieldno]['size'] ) $fields[$fieldno]['size'] = $len; - - if ( trim($strTmp) != '' ) - { - if ( $fields[$fieldno]['SPSStype'] == 'F' && (isNumericExtended($strTmp) === FALSE || $fields[$fieldno]['size'] > 16) ) - { - $fields[$fieldno]['SPSStype'] = 'A'; - } - } - } - - $fieldno++; - } - } - $result->close(); - - $errors = ""; - $i = 1; - foreach ( $fields as $field ) - { - if ( $field['SPSStype'] == 'DATETIME23.2' ) $field['size']=''; - - if ( $field['LStype'] == 'N' || $field['LStype'] == 'K' ) - { - $field['size'] .= '.' . ($field['size'] - 1); - } - - switch ( $field['SPSStype'] ) - { - case 'F': - $type = "numeric"; - break; - case 'A': - $type = "character"; - break; - case 'DATETIME23.2': - case 'SDATE': - $type = "character"; - //@TODO set $type to format for date - break; - } - - if ( ! $field['hide'] ) - { - echo("# LimeSurvey Field type: $field[SPSStype]\n"); - echo "data[, " . $i . "] <- " - . "as.$type(data[, " . $i . "])\n"; - - echo 'attributes(data)$variable.labels[' . $i . '] <- "' - . addslashes( - htmlspecialchars_decode( - mb_substr( - stripTagsFull( - $field['VariableLabel'] - ), 0, $length_varlabel - ) - ) - ) - . '"' . "\n"; - - // Create the value Labels! - if ( isset($field['answers']) ) - { - $answers = $field['answers']; - - //print out the value labels! - echo 'data[, ' . $i .'] <- factor(data[, ' . $i . '], levels=c('; - - $str = ""; - foreach ( $answers as $answer ) - { - if ( $field['SPSStype'] == "F" && isNumericExtended($answer['code']) ) - { - $str .= ",{$answer['code']}"; - } - else - { - $str .= ",\"{$answer['code']}\""; - } - } - - $str = mb_substr($str, 1); - echo $str . '),labels=c('; - - $str = ""; - foreach ( $answers as $answer ) - { - $str .= ", \"{$answer['value']}\""; - } - - $str = mb_substr($str, 2); // Remove leading comma and space - - if ( $field['scale'] !== '' && $field['scale'] == 2 ) - { - $scale = ", ordered=TRUE"; - } - else - { - $scale = ""; - } - - echo("{$str}){$scale})\n"); - } - - //Rename the Variables (in case somethings goes wrong, we still have the OLD values - if ( isset($field['sql_name']) ) - { - $ftitle = $field['title']; - if (!preg_match ("/^([a-z]|[A-Z])+.*$/", $ftitle)) - { - $ftitle = "q_" . $ftitle; - } - - $ftitle = str_replace(array("-",":",";","!"), array("_hyph_","_dd_","_dc_","_excl_"), $ftitle); - - if ( ! $field['hide'] ) - { - if ( $ftitle != $field['title'] ) - { - $errors .= "# Variable name was incorrect and was changed from {$field['title']} to $ftitle .\n"; - } - - echo "names(data)[" . $i . "] <- " - . "\"". $ftitle . "\"\n"; // added \n - } - - $i++; - } - else - { - echo "#sql_name not set\n"; - } - } - else - { - echo "#Field hidden\n"; - } - - echo "\n"; - - } // end foreach - echo $errors; - exit; - } - - - } - public function vvexport() { $iSurveyId = sanitize_int(Yii::app()->request->getParam('surveyid')); diff --git a/application/core/plugins/ExportR/RDataWriter.php b/application/core/plugins/ExportR/RDataWriter.php index 10d8ededdfb..2bf1d03028f 100644 --- a/application/core/plugins/ExportR/RDataWriter.php +++ b/application/core/plugins/ExportR/RDataWriter.php @@ -1,8 +1,14 @@ csvFilename = 'survey_' . $survey->id .'_R_data_file.csv'; + // Skip the first line with headers + $this->doHeaders = false; + $oOptions->answerFormat = "short"; // force answer codes $oOptions->convertN = true; $oOptions->nValue = 1; diff --git a/application/helpers/admin/export/CsvWriter.php b/application/helpers/admin/export/CsvWriter.php index 3a53ac47afa..fb37e302dd7 100644 --- a/application/helpers/admin/export/CsvWriter.php +++ b/application/helpers/admin/export/CsvWriter.php @@ -8,6 +8,21 @@ class CsvWriter extends Writer * The open filehandle */ private $file = null; + + /** + * The filename to use for the resulting file when output = display + * + * @var string + */ + protected $csvFilename = ''; + + /** + * Should headers be output? For example spss and r export use more or less + * the same output but do not need headers at all. + * + * @var boolean + */ + protected $doHeaders = true; function __construct() { @@ -19,10 +34,10 @@ function __construct() public function init(SurveyObj $survey, $sLanguageCode, FormattingOptions $oOptions) { parent::init($survey, $sLanguageCode, $oOptions); - if ($oOptions->output=='display') { - header("Content-Disposition: attachment; filename=results-survey".$survey->id.".csv"); - header("Content-type: text/comma-separated-values; charset=UTF-8"); - } elseif ($oOptions->output == 'file') { + + $this->csvFilename = "results-survey".$survey->id.".csv"; + + if ($oOptions->output == 'file') { $this->file = fopen($this->filename, 'w'); } @@ -33,14 +48,22 @@ protected function outputRecord($headers, $values, FormattingOptions $oOptions) $sRecord=''; if(!$this->hasOutputHeader) { - $index = 0; - foreach ($headers as $header) - { - $headers[$index] = $this->csvEscape($header); - $index++; + if ($oOptions->output=='display') { + header("Content-Disposition: attachment; filename=" . $this->csvFilename); + header("Content-type: text/comma-separated-values; charset=UTF-8"); + } + + // If we don't want headers in our csv, for example in exports like r/spss etc. we suppress the header by setting this switch in the init + if ($this->doHeaders == true) { + $index = 0; + foreach ($headers as $header) + { + $headers[$index] = $this->csvEscape($header); + $index++; + } + //Output the header...once and only once. + $sRecord.=implode($this->separator, $headers) . PHP_EOL; } - //Output the header...once and only once. - $sRecord.=implode($this->separator, $headers); $this->hasOutputHeader = true; } //Output the values. @@ -50,7 +73,7 @@ protected function outputRecord($headers, $values, FormattingOptions $oOptions) $values[$index] = $this->csvEscape($value); $index++; } - $sRecord.=PHP_EOL.implode($this->separator, $values); + $sRecord.= implode($this->separator, $values) . PHP_EOL; if ($oOptions->output=='display') { echo $sRecord; diff --git a/application/views/admin/responses/browsemenubar_view.php b/application/views/admin/responses/browsemenubar_view.php index 2366bd0103c..0ad1eae4846 100644 --- a/application/views/admin/responses/browsemenubar_view.php +++ b/application/views/admin/responses/browsemenubar_view.php @@ -70,7 +70,7 @@ <?php $clang->eT(" /> - + <?php $clang->eT("Export results to a R data file"); ?>