|
From: <gem...@li...> - 2013-04-15 12:15:16
|
Revision: 1229
http://sourceforge.net/p/gemstracker/code/1229
Author: mennodekker
Date: 2013-04-15 12:15:09 +0000 (Mon, 15 Apr 2013)
Log Message:
-----------
Moved SPSS export to batch, now outputs a zip file holding the two files.
Modified Paths:
--------------
trunk/library/classes/Gems/Default/ExportAction.php
trunk/library/classes/Gems/Export/Excel.php
trunk/library/classes/Gems/Export/ExportBatchInterface.php
trunk/library/classes/Gems/Export/Spss.php
Modified: trunk/library/classes/Gems/Default/ExportAction.php
===================================================================
--- trunk/library/classes/Gems/Default/ExportAction.php 2013-04-15 10:30:26 UTC (rev 1228)
+++ trunk/library/classes/Gems/Default/ExportAction.php 2013-04-15 12:15:09 UTC (rev 1229)
@@ -350,7 +350,7 @@
$controller->addMessage($messages);
- if (!empty($files)) {
+ if (!empty($files) && array_key_exists('file', $files) && file_exists(GEMS_ROOT_DIR . '/var/tmp/' . $files['file'])) {
// Forward to download action
$this->_session->exportFile = $files;
$this->_reroute(array('action'=>'download'));
Modified: trunk/library/classes/Gems/Export/Excel.php
===================================================================
--- trunk/library/classes/Gems/Export/Excel.php 2013-04-15 10:30:26 UTC (rev 1228)
+++ trunk/library/classes/Gems/Export/Excel.php 2013-04-15 12:15:09 UTC (rev 1229)
@@ -101,53 +101,22 @@
*/
public function handleExport($data, $survey, $answers, $answerModel, $language)
{
- $questions = $survey->getQuestionList($language);
- if (isset($data[$this->getName()])) {
- $options = $data[$this->getName()];
- if (isset($options['format'])) {
- $options = $options['format'];
- }
- } else {
- $options = array();
- }
-
- if (in_array('formatVariable', $options)) {
- //@@TODO This breaks date formatting, think of a way to fix this, check out the spss exports for
- //a more direct export, also check UTF-8 differences between view / direct output
- foreach ($answers[0] as $key => $value) {
- if (isset($questions[$key])) {
- $headers[0][$key] = $questions[$key];
- } else {
- $headers[0][$key] = $key;
- }
- }
- } else {
- $headers[0] = array_keys($answers[0]);
- }
- $answers = array_merge($headers, $answers);
-
- if (in_array('formatAnswer', $options)) {
- $answers = new Gems_FormattedData($answers, $answerModel);
- }
-
- $this->view->result = $answers;
- $this->view->filename = $survey->getName() . '.xls';
- $this->view->setScriptPath(GEMS_LIBRARY_DIR . '/views/scripts');
- $this->export->controller->render('excel',null,true);
+ // We only do batch export
+ return;
}
/**
* This method handles the export with the given options
*
- * We open the file and add tasks to the batch to export in steps of 500 records.
+ * We open the file and add tasks to the batch to export in steps of 500 records.
* This should be small enough to not run out of time/memory.
- *
+ *
* We make use of the Export_ExportCommand to forward calls to this class.
- * Extra methods in this class are
+ * Extra methods in this class are
* handleExportBatchStep Exports the records
- * and
+ * and
* handleExportBatchFinalize Write the footer to the file
- *
+ *
* @param Gems_Task_TaskRunnerBatch $batch The batch to start
* @param array $filter The filter to use
* @param string $language The language used / to use for the export
@@ -158,7 +127,14 @@
$answerCount = $survey->getRawTokenAnswerRowsCount($filter);
$answers = $survey->getRawTokenAnswerRows(array('limit'=>1,'offset'=>1) + $filter); // Limit to one response
$filename = $survey->getName() . '.xls';
-
+
+ if (count($answers) === 0) {
+ $noData = sprintf($this->_('No %s found.'), $this->_('data'));
+ $answers = array($noData => $noData);
+ } else {
+ $answers = reset($answers);
+ }
+
$files['file']= 'export-' . md5(time() . rand());
$files['headers'][] = "Content-Type: application/download";
$files['headers'][] = "Content-Disposition: attachment; filename=\"" . $filename . "\"";
@@ -166,10 +142,10 @@
$files['headers'][] = "Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT";
$files['headers'][] = "Cache-Control: must-revalidate, post-check=0, pre-check=0";
$files['headers'][] = "Pragma: cache"; // HTTP/1.0
-
+
$batch->setMessage('file', $files);
$batch->setMessage('export-progress', $this->_('Initializing export'));
-
+
$f = fopen(GEMS_ROOT_DIR . '/var/tmp/' . $files['file'], 'w');
fwrite($f, '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">
<head>
@@ -188,7 +164,7 @@
font-weight: bold;
padding: 3px 8px;
border: .5pt solid #000000;
- background: #c0c0c0
+ background: #c0c0c0
}
tr td {
padding: 3px 8px;
@@ -198,7 +174,7 @@
</style>
</head>
<body>');
-
+
if (isset($data[$this->getName()])) {
$options = $data[$this->getName()];
if (isset($options['format'])) {
@@ -213,7 +189,7 @@
$questions = $survey->getQuestionList($language);
//@@TODO This breaks date formatting, think of a way to fix this, check out the spss exports for
//a more direct export, also check UTF-8 differences between view / direct output
- foreach ($answers[0] as $key => $value) {
+ foreach ($answers as $key => $value) {
if (isset($questions[$key])) {
$headers[$key] = $questions[$key];
} else {
@@ -221,9 +197,9 @@
}
}
} else {
- $headers = array_keys($answers[0]);
+ $headers = array_keys($answers);
}
-
+
//Only for the first row: output headers
$output = "<table>\r\n";
$output .= "\t<thead>\r\n";
@@ -234,9 +210,9 @@
$output .= "\t\t</tr>\r\n";
$output .= "\t</thead>\r\n";
$output .= "\t<tbody>\r\n";
-
+
fwrite($f, $output);
-
+
fclose($f);
// Add as many steps as needed
$current = 0;
@@ -245,25 +221,25 @@
$filter['limit'] = $step;
$filter['offset'] = $current;
$batch->addTask('Export_ExportCommand', $data['type'], 'handleExportBatchStep', $data, $filter, $language);
- $current = $current + $step;
+ $current = $current + $step;
} while ($current < $answerCount);
-
+
$batch->addTask('Export_ExportCommand', $data['type'], 'handleExportBatchFinalize');
-
+
return;
}
-
+
public function handleExportBatchStep($data, $filter, $language)
{
$batch = $this->_batch;
$files = $batch->getMessage('file', array());
- $survey = $this->loader->getTracker()->getSurvey($data['sid']);
- $answers = $survey->getRawTokenAnswerRows($filter);
+ $survey = $this->loader->getTracker()->getSurvey($data['sid']);
+ $answers = $survey->getRawTokenAnswerRows($filter);
$answerModel = $survey->getAnswerModel($language);
//Now add the organization id => name mapping
$answerModel->set('organizationid', 'multiOptions', $this->loader->getCurrentUser()->getAllowedOrganizations());
$batch->setMessage('export-progress', sprintf($this->_('Exporting records %s and up'), $filter['offset']));
-
+
if (isset($data[$this->getName()])) {
$options = $data[$this->getName()];
if (isset($options['format'])) {
@@ -272,11 +248,11 @@
} else {
$options = array();
}
-
+
if (in_array('formatAnswer', $options)) {
$answers = new Gems_FormattedData($answers, $answerModel);
}
-
+
$f = fopen(GEMS_ROOT_DIR . '/var/tmp/' . $files['file'], 'a');
foreach($answers as $answer)
{
@@ -289,7 +265,7 @@
}
fclose($f);
}
-
+
public function handleExportBatchFinalize()
{
$files = $this->_batch->getMessage('file', array());
@@ -300,6 +276,6 @@
</body>
</html>');
fclose($f);
- }
-
+ }
+
}
\ No newline at end of file
Modified: trunk/library/classes/Gems/Export/ExportBatchInterface.php
===================================================================
--- trunk/library/classes/Gems/Export/ExportBatchInterface.php 2013-04-15 10:30:26 UTC (rev 1228)
+++ trunk/library/classes/Gems/Export/ExportBatchInterface.php 2013-04-15 12:15:09 UTC (rev 1229)
@@ -51,6 +51,11 @@
*
* Normally this will initialize the file to download and set up as much
* steps as needed and the final job to close the file.
+ *
+ * To offer a file for download, add a message with the key 'file' to the
+ * batch. The message must be an array of 'headers' that contains an array
+ * of headers to set for the download and 'file' that holds the path to the
+ * file relative to GEMS_ROOT_DIR . '/var/tmp/'
*
* @param Gems_Task_TaskRunnerBatch $batch The batch to start
* @param array $filter The filter to use
Modified: trunk/library/classes/Gems/Export/Spss.php
===================================================================
--- trunk/library/classes/Gems/Export/Spss.php 2013-04-15 10:30:26 UTC (rev 1228)
+++ trunk/library/classes/Gems/Export/Spss.php 2013-04-15 12:15:09 UTC (rev 1229)
@@ -45,46 +45,24 @@
* @license New BSD License
* @since Class available since version 1.5
*/
-class Gems_Export_Spss extends Gems_Export_ExportAbstract
+class Gems_Export_Spss extends Gems_Export_ExportAbstract implements Gems_Export_ExportBatchInterface
{
/**
* When no other size available in the answermodel, this will be used
* for the size of alphanumeric types
- *
+ *
* @var int
*/
public $defaultAlphaSize = 64;
-
+
/**
* When no other size available in the answermodel, this will be used
* for the size of numeric types
- *
+ *
* @var int
*/
public $defaultNumericSize = 5;
-
- /**
- * Set response headers and clean output
- *
- * @param string $filename
- * @return Zend_Controller_Response_Abstract
- */
- private function _doHeaders($filename)
- {
- $controller = $this->export->controller;
- $controller->getHelper('layout')->disableLayout();
- $controller->getHelper('viewRenderer')->setNoRender(true);
- $response = $controller->getResponse();
- $response->clearAllHeaders();
- $response->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"', true)
- ->setHeader('Content-type', 'text/plain; charset=UTF-8', true)
- ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true)
- ->setHeader('Pragma', 'public', true);
-
- return $response;
- }
-
public function getDefaults()
{
return array('file' => 'data');
@@ -92,15 +70,8 @@
public function getFormElements(&$form, &$data)
{
- $element = new Zend_Form_Element_Radio('file');
- $element->setLabel($this->_('Which file'));
- $element->setMultiOptions(array(
- 'syntax' => $this->_('syntax'),
- 'data' => $this->_('data')));
- $elements[] = $element;
-
$element = new MUtil_Form_Element_Exhibitor('help');
- $element->setValue($this->_('You need both a syntax and a data file. After downloading, open the syntax file in SPSS and modify the line /FILE="filename.dat" to include the full path to your data file. Then choose Run -> All to execute the syntax.'));
+ $element->setValue($this->_('Extract both syntax and data file form the archive. Open the syntax file in SPSS and modify the line /FILE="filename.dat" to include the full path to your data file. Then choose Run -> All to execute the syntax.'));
$elements[] = $element;
return $elements;
@@ -125,40 +96,94 @@
*/
public function handleExport($data, $survey, $answers, $answerModel, $language)
{
- if (isset($data[$this->getName()])) {
- $options = $data[$this->getName()];
- } else {
- $options = array();
- }
+ // We only do batch export
+ return;
+ }
- if (isset($options['file'])) {
- if ($options['file'] == 'syntax') {
- $this->handleExportSyntax($data, $survey, $answers, $answerModel, $language);
- } else {
- $this->handleExportData($data, $survey, $answers, $answerModel, $language);
- }
- }
+ /**
+ * Formatting of strings for SPSS export. Enclose in single quotes and escape single quotes
+ * with a single quote
+ *
+ * Example:
+ * This isn't hard to understand
+ * ==>
+ * 'This isn''t hard to understand'
+ *
+ * @param type $input
+ * @return string
+ */
+ public function formatString($input)
+ {
+ $output = strip_tags($input);
+ $output = str_replace(array("'", "\r", "\n"), array("''", ' ', ' '), $output);
+ $output = "'" . $output . "'";
+ return $output;
}
/**
- * This method handles the export with the given options for a syntax file
+ * Make sure the $input fieldname is correct for usage in SPSS
*
- * The method takes care of rendering the right script by using $this->export->controller to
- * access the controller object.
+ * Should start with alphanum, and contain no spaces
*
- * @param array $data The formdata
- * @param Gems_Tracker_Survey $survey The survey object we are exporting
- * @param array $answers The array of answers
- * @param MUtil_Model_ModelAbstract $answerModel The modified answermodel that includes info about extra attributes
- * @param string $language The language used / to use for the export
+ * @param string $input
+ * @return string
*/
- public function handleExportData($data, $survey, $answers, $answerModel, $language)
+ public function fixName($input)
{
- $filename = $survey->getName() . '.dat';
- $response = $this->_doHeaders($filename);
+ if (!preg_match("/^([a-z]|[A-Z])+.*$/", $input)) {
+ $input = "q_" . $input;
+ }
+ $input = str_replace(array(" ", "-", ":", ";", "!", "/", "\\", "'"), array("_", "_hyph_", "_dd_", "_dc_", "_excl_", "_fs_", "_bs_", '_qu_'), $input);
+ return $input;
+ }
- //We should create a model with the transformations we need
- //think of date translations, numers and strings
+ public function handleExportBatch($batch, $filter, $language, $data)
+ {
+ $survey = $this->loader->getTracker()->getSurvey($data['sid']);
+ $answerCount = $survey->getRawTokenAnswerRowsCount($filter);
+ $answers = $survey->getRawTokenAnswerRows(array('limit'=>1,'offset'=>1) + $filter); // Limit to one response
+
+ if (count($answers) === 0) {
+ $noData = sprintf($this->_('No %s found.'), $this->_('data'));
+ $answers = array($noData => $noData);
+ } else {
+ $answers = reset($answers);
+ }
+
+ $file = 'export-' . md5(time() . rand());
+
+ // Now create syntax and data file
+ $f = fopen(GEMS_ROOT_DIR . '/var/tmp/' . $file . '.dat', 'w');
+ fclose($f);
+ $f = fopen(GEMS_ROOT_DIR . '/var/tmp/' . $file . '.sps', 'w');
+ fclose($f);
+
+ // Add as many steps as needed
+ $current = 0;
+ $step = 500;
+ do {
+ $filter['limit'] = $step;
+ $filter['offset'] = $current;
+ $batch->addTask('Export_ExportCommand', $data['type'], 'handleExportBatchStepData', $data, $filter, $language, $file);
+ $current = $current + $step;
+ } while ($current < $answerCount);
+
+ $batch->addTask('Export_ExportCommand', $data['type'], 'handleExportBatchStepSyntax', $data, $filter, $language, $file);
+
+ $batch->addTask('Export_ExportCommand', $data['type'], 'handleExportBatchFinalize', $data, $file);
+ }
+
+ public function handleExportBatchStepData($data, $filter, $language, $file)
+ {
+ $batch = $this->_batch;
+ $survey = $this->loader->getTracker()->getSurvey($data['sid']);
+ $answers = $survey->getRawTokenAnswerRows($filter);
+ $answerModel = $survey->getAnswerModel($language);
+
+ //Now add the organization id => name mapping
+ $answerModel->set('organizationid', 'multiOptions', $this->loader->getCurrentUser()->getAllowedOrganizations());
+ $batch->setMessage('export-progress', sprintf($this->_('Exporting records %s and up'), $filter['offset']));
+
$answerRow = reset($answers);
$spssModel = new Gems_Export_ExportModel();
foreach ($answerRow as $key => $value) {
@@ -194,35 +219,35 @@
$spssModel->set($key, $options);
}
//Now apply the model to the answers
- $answers = new Gems_FormattedData($answers, $spssModel);
+ $answers = new Gems_FormattedData($answers, $spssModel);
+ $f = fopen(GEMS_ROOT_DIR . '/var/tmp/' . $file . '.dat', 'a');
+
//And output the data
foreach ($answers as $answerRow) {
$resultRow = implode(',', $answerRow);
- $response->appendBody($resultRow . "\n");
+ fwrite($f, $resultRow . "\n");
}
+
+ fclose($f);
}
+
+ public function handleExportBatchStepSyntax($data, $filter, $language, $file)
+ {
+ $survey = $this->loader->getTracker()->getSurvey($data['sid']);
+ $answers = $survey->getRawTokenAnswerRows(array('limit'=>1,'offset'=>1) + $filter); // Limit to one response
+ $answerModel = $survey->getAnswerModel($language);
- /**
- * This method handles the export with the given options for a syntax file
- *
- * The method takes care of rendering the right script by using $this->export->controller to
- * access the controller object.
- *
- * @param array $data The formdata
- * @param Gems_Tracker_Survey $survey The survey object we are exporting
- * @param array $answers The array of answers
- * @param MUtil_Model_ModelAbstract $answerModel The modified answermodel that includes info about extra attributes
- * @param string $language The language used / to use for the export
- */
- protected function handleExportSyntax($data, $survey, $answers, $answerModel, $language)
- {
- $filename = $survey->getName() . '.sps';
+ //Now add the organization id => name mapping
+ $answerModel->set('organizationid', 'multiOptions', $this->loader->getCurrentUser()->getAllowedOrganizations());
+ $this->_batch->setMessage('export-progress', sprintf($this->_('Exporting records %s and up'), $filter['offset']));
+
+ $f = fopen(GEMS_ROOT_DIR . '/var/tmp/' . $file . '.sps', 'a');
+
$filenameDat = $survey->getName() . '.dat';
- $response = $this->_doHeaders($filename);
//first output our script
- $response->appendBody(
+ fwrite($f,
"SET UNICODE=ON.
GET DATA
/TYPE=TXT
@@ -234,6 +259,8 @@
/FIRSTCASE=1
/IMPORTCASE=ALL
/VARIABLES=");
+
+
$answerRow = reset($answers);
$labels = array();
$types = array();
@@ -283,68 +310,59 @@
if (isset($questions[$key])) {
$labels[$key] = $questions[$key];
}
- $response->appendBody("\n " . $fixedNames[$key] . ' ' . $type);
+ fwrite($f, "\n " . $fixedNames[$key] . ' ' . $type);
}
- $response->appendBody(".\nCACHE.\nEXECUTE.\n");
- $response->appendBody("\n*Define variable labels.\n");
+ fwrite($f, ".\nCACHE.\nEXECUTE.\n");
+ fwrite($f, "\n*Define variable labels.\n");
foreach ($labels as $key => $label) {
$label = $this->formatString($label);
- $response->appendBody("VARIABLE LABELS " . $fixedNames[$key] . " " . $label . "." . "\n");
+ fwrite($f, "VARIABLE LABELS " . $fixedNames[$key] . " " . $label . "." . "\n");
}
- $response->appendBody("\n*Define value labels.\n");
+ fwrite($f, "\n*Define value labels.\n");
foreach ($answerRow as $key => $value) {
if ($options = $answerModel->get($key, 'multiOptions')) {
- $response->appendBody('VALUE LABELS ' . $fixedNames[$key]);
+ fwrite($f, 'VALUE LABELS ' . $fixedNames[$key]);
foreach ($options as $option => $label) {
$label = $this->formatString($label);
if ($types[$key] == 'F') {
//Numeric
- $response->appendBody("\n" . $option . ' ' . $label);
+ fwrite($f, "\n" . $option . ' ' . $label);
} else {
//String
- $response->appendBody("\n" . '"' . $option . '" ' . $label);
+ fwrite($f, "\n" . '"' . $option . '" ' . $label);
}
}
- $response->appendBody(".\n\n");
+ fwrite($f, ".\n\n");
}
}
+
+ fclose($f);
}
- /**
- * Formatting of strings for SPSS export. Enclose in single quotes and escape single quotes
- * with a single quote
- *
- * Example:
- * This isn't hard to understand
- * ==>
- * 'This isn''t hard to understand'
- *
- * @param type $input
- * @return string
- */
- public function formatString($input)
+ public function handleExportBatchFinalize($data, $file)
{
- $output = strip_tags($input);
- $output = str_replace(array("'", "\r", "\n"), array("''", ' ', ' '), $output);
- $output = "'" . $output . "'";
- return $output;
+ $survey = $this->loader->getTracker()->getSurvey($data['sid']);
+ $filename = $survey->getName() . '.zip';
+ $zipFile = 'export-' . md5(time() . rand());
+
+ $zipArchive = new ZipArchive();
+ $zipArchive->open( GEMS_ROOT_DIR . '/var/tmp/' . $zipFile, ZipArchive::CREATE);
+ $zipArchive->addFile(GEMS_ROOT_DIR . '/var/tmp/' . $file . '.dat', $survey->getName() . '.dat');
+ $zipArchive->addFile(GEMS_ROOT_DIR . '/var/tmp/' . $file . '.sps', $survey->getName() . '.sps');
+ $zipArchive->close();
+ unlink(GEMS_ROOT_DIR . '/var/tmp/' . $file . '.dat');
+ unlink(GEMS_ROOT_DIR . '/var/tmp/' . $file . '.sps');
+
+ $files = array();
+ $files['file'] = $zipFile;
+ $files['headers'][] = "Content-Type: application/download";
+ $files['headers'][] = "Content-Disposition: attachment; filename=\"" . $filename . "\"";
+ $files['headers'][] = "Expires: Mon, 26 Jul 1997 05:00:00 GMT"; // Date in the past
+ $files['headers'][] = "Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT";
+ $files['headers'][] = "Cache-Control: must-revalidate, post-check=0, pre-check=0";
+ $files['headers'][] = "Pragma: cache"; // HTTP/1.0
+
+ $this->_batch->setMessage('file', $files);
}
-
- /**
- * Make sure the $input fieldname is correct for usage in SPSS
- *
- * Should start with alphanum, and contain no spaces
- *
- * @param string $input
- * @return string
- */
- public function fixName($input)
- {
- if (!preg_match("/^([a-z]|[A-Z])+.*$/", $input)) {
- $input = "q_" . $input;
- }
- $input = str_replace(array(" ", "-", ":", ";", "!", "/", "\\", "'"), array("_", "_hyph_", "_dd_", "_dc_", "_excl_", "_fs_", "_bs_", '_qu_'), $input);
- return $input;
- }
}
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|