From: <gem...@li...> - 2013-04-10 08:33:12
|
Revision: 1216 http://sourceforge.net/p/gemstracker/code/1216 Author: mennodekker Date: 2013-04-10 08:33:06 +0000 (Wed, 10 Apr 2013) Log Message: ----------- Fixed layout for compliance overview with round descriptions/icons Modified Paths: -------------- trunk/library/classes/Gems/Default/ComplianceAction.php trunk/library/classes/Gems/Snippets/Tracker/Compliance/ComplianceTableSnippet.php Modified: trunk/library/classes/Gems/Default/ComplianceAction.php =================================================================== --- trunk/library/classes/Gems/Default/ComplianceAction.php 2013-04-09 14:36:33 UTC (rev 1215) +++ trunk/library/classes/Gems/Default/ComplianceAction.php 2013-04-10 08:33:06 UTC (rev 1216) @@ -107,7 +107,7 @@ } $select = $this->db->select(); - $select->from('gems__rounds', array('gro_id_round', 'gro_id_order', 'gro_round_description')) + $select->from('gems__rounds', array('gro_id_round', 'gro_id_order', 'gro_round_description', 'gro_icon_file')) ->joinInner('gems__surveys', 'gro_id_survey = gsu_id_survey', array('gsu_survey_name')) ->where('gro_id_track = ?', $filter['gr2t_id_track']) ->order('gro_id_order'); @@ -147,7 +147,8 @@ $transformer->set($name, 'label', MUtil_Lazy::call('substr', $row['gsu_survey_name'], 0, 2), 'description', sprintf("%s\n[%s]", $row['gsu_survey_name'], $row['gro_round_description']), 'noSort', true, - 'round', $row['gro_round_description'] + 'round', $row['gro_round_description'], + 'roundIcon', $row['gro_icon_file'] ); $transformer->set('tok_' . $row['gro_id_round']); } Modified: trunk/library/classes/Gems/Snippets/Tracker/Compliance/ComplianceTableSnippet.php =================================================================== --- trunk/library/classes/Gems/Snippets/Tracker/Compliance/ComplianceTableSnippet.php 2013-04-09 14:36:33 UTC (rev 1215) +++ trunk/library/classes/Gems/Snippets/Tracker/Compliance/ComplianceTableSnippet.php 2013-04-10 08:33:06 UTC (rev 1216) @@ -78,6 +78,9 @@ if ($showMenuItem = $this->getShowMenuItem()) { $bridge->addItemLink($showMenuItem->toActionLinkLower($this->request, $bridge)); } + + // Initialize alter + $alternateClass = new MUtil_Lazy_Alternate(array('odd', 'even')); foreach($model->getItemsOrdered() as $name) { if ($label = $model->get($name, 'label')) { @@ -86,13 +89,26 @@ $span++; $class = null; } else { - $th->append($cRound); + // If the round has an icon, show the icon else just 'R' since + // complete round description messes up the display + if (!empty($cIcon)) { + $content = MUtil_Html_ImgElement::imgFile($cIcon, array( + 'alt' => $cRound, + 'title' => $cRound + )); + } else { + $content = 'R'; + } + $th->append($content); + $th->title = $cRound; $th->colspan = $span; - $span = 1; - $cRound = $round; - $class = 'newRound'; - $th = $th_row->td(array('class' => $class)); + $span = 1; + $cRound = $round; + $cIcon = $model->get($name, 'roundIcon'); + $class = 'newRound'; + $thClass = $class .' ' . $alternateClass; // Add alternate class only for th + $th = $th_row->td(array('class' => $thClass)); } if ($model->get($name, 'noSort')) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <gem...@li...> - 2013-04-10 09:59:11
|
Revision: 1218 http://sourceforge.net/p/gemstracker/code/1218 Author: mennodekker Date: 2013-04-10 09:59:07 +0000 (Wed, 10 Apr 2013) Log Message: ----------- Preparation for batch export of results, allow for limit and offset Retrieval of rowcount for export is now more efficient Modified Paths: -------------- trunk/library/classes/Gems/Default/ExportAction.php trunk/library/classes/Gems/Tracker/Source/LimeSurvey1m9Database.php trunk/library/classes/Gems/Tracker/Source/SourceAbstract.php trunk/library/classes/Gems/Tracker/Source/SourceInterface.php trunk/library/classes/Gems/Tracker/Survey.php Modified: trunk/library/classes/Gems/Default/ExportAction.php =================================================================== --- trunk/library/classes/Gems/Default/ExportAction.php 2013-04-10 08:51:22 UTC (rev 1217) +++ trunk/library/classes/Gems/Default/ExportAction.php 2013-04-10 09:59:07 UTC (rev 1218) @@ -114,7 +114,7 @@ //$filter['organizationid'] = '-1'; } $filter['consentcode'] = array_diff((array) $this->util->getConsentTypes(), (array) $this->util->getConsentRejected()); - + // Gems_Tracker::$verbose = true; return $filter; } @@ -161,10 +161,12 @@ if (isset($data['sid'])) { $survey = $this->loader->getTracker()->getSurvey(intval($data['sid'])); $filter = $this->_getFilter($data); - $answers = $survey->getRawTokenAnswerRows($filter); + //$answers = $survey->getRawTokenAnswerRows($filter); + //$recordCount = count($answers); + $recordCount = $survey->getRawTokenAnswerRowsCount($filter); $element = new MUtil_Form_Element_Exhibitor('records'); - $element->setValue(sprintf($this->_('%s records found.'), count($answers))); + $element->setValue(sprintf($this->_('%s records found.'), $recordCount)); $elements[] = $element; } Modified: trunk/library/classes/Gems/Tracker/Source/LimeSurvey1m9Database.php =================================================================== --- trunk/library/classes/Gems/Tracker/Source/LimeSurvey1m9Database.php 2013-04-10 08:51:22 UTC (rev 1217) +++ trunk/library/classes/Gems/Tracker/Source/LimeSurvey1m9Database.php 2013-04-10 09:59:07 UTC (rev 1218) @@ -828,7 +828,80 @@ if (null === $sourceSurveyId) { $sourceSurveyId = $this->_getSid($surveyId); } + $select = $this->getRawTokenAnswerRowsSelect($filter, $surveyId, $sourceSurveyId); + + //Now process the filters + $lsSurveyTable = $this->_getSurveyTableName($sourceSurveyId); + $tokenField = $lsSurveyTable . '.token'; + if (is_array($filter)) { + //first preprocess the tokens + if (isset($filter['token'])) { + foreach ((array) $filter['token'] as $key => $tokenId) { + $token = $this->_getToken($tokenId); + $originals[$token] = $tokenId; + $filter[$tokenField][$key] = $token; + } + unset($filter['token']); + } + } + + $rows = $select->query()->fetchAll(Zend_Db::FETCH_ASSOC); + $results = array(); + //@@TODO: check if we really need this, or can just change the 'token' field to have the 'original' + // this way other sources that don't perform changes on the token field don't have to loop + // over this field. The survey(answer)model could possibly perform the translation for this source + if ($rows) { + $map = $this->_getFieldMap($sourceSurveyId); + if (isset($filter[$tokenField])) { + foreach ($rows as $values) { + $token = $originals[$values['token']]; + $results[$token] = $map->mapKeysToTitles($values); + } + return $results; + } else { + //@@TODO If we do the mapping in the select statement, maybe we can gain some performance here + foreach ($rows as $values) { + $results[] = $map->mapKeysToTitles($values); + } + return $results; + } + } + + return array(); + } + + /** + * Returns the recordcount for a given filter + * + * @param array $filter filter array + * @param int $surveyId Gems Survey Id + * @param string $sourceSurveyId Optional Survey Id used by source + * @return int + */ + public function getRawTokenAnswerRowsCount(array $filter, $surveyId, $sourceSurveyId = null) { + $select = $this->getRawTokenAnswerRowsSelect($filter, $surveyId, $sourceSurveyId); + + $p = new Zend_Paginator_Adapter_DbSelect($select); + MUtil_Echo::track($p->getCountSelect()->__toString()); + $count = $p->getCountSelect()->query()->fetchColumn(); + + return $count; + } + + /** + * Get the select object to use for RawTokenAnswerRows + * + * @param array $filter + * @param type $surveyId + * @param type $sourceSurveyId + * @return Zend_Db_Select + */ + public function getRawTokenAnswerRowsSelect(array $filter, $surveyId, $sourceSurveyId = null) { + if (null === $sourceSurveyId) { + $sourceSurveyId = $this->_getSid($surveyId); + } + $lsDb = $this->getSourceDatabase(); $lsSurveyTable = $this->_getSurveyTableName($sourceSurveyId); $lsTokenTable = $this->_getTokenTableName($sourceSurveyId); @@ -863,6 +936,9 @@ } } + // Add limit / offset to select and remove from filter + $this->filterLimitOffset($filter, $select); + foreach ($filter as $field => $values) { $field = $lsDb->quoteIdentifier($field); if (is_array($values)) { @@ -876,29 +952,7 @@ MUtil_Echo::r($select->__toString(), 'Select'); } - $rows = $select->query()->fetchAll(Zend_Db::FETCH_ASSOC); - $results = array(); - //@@TODO: check if we really need this, or can just change the 'token' field to have the 'original' - // this way other sources that don't perform changes on the token field don't have to loop - // over this field. The survey(answer)model could possibly perform the translation for this source - if ($rows) { - $map = $this->_getFieldMap($sourceSurveyId); - if (isset($filter[$tokenField])) { - foreach ($rows as $values) { - $token = $originals[$values['token']]; - $results[$token] = $map->mapKeysToTitles($values); - } - return $results; - } else { - //@@TODO If we do the mapping in the select statement, maybe we can gain some performance here - foreach ($rows as $values) { - $results[] = $map->mapKeysToTitles($values); - } - return $results; - } - } - - return array(); + return $select; } /** Modified: trunk/library/classes/Gems/Tracker/Source/SourceAbstract.php =================================================================== --- trunk/library/classes/Gems/Tracker/Source/SourceAbstract.php 2013-04-10 08:51:22 UTC (rev 1217) +++ trunk/library/classes/Gems/Tracker/Source/SourceAbstract.php 2013-04-10 09:59:07 UTC (rev 1218) @@ -228,6 +228,28 @@ $this->_sourceData['gso_ls_table_prefix'] . $tableName; } + + /** + * Extract limit and offset from the filter and add it to a select + * + * @param array $filter + * @param Zend_Db_Select $select + */ + protected function filterLimitOffset(&$filter, $select) + { + $limit = null; + $offset = null; + + if (array_key_exists('limit', $filter)) { + $limit = (int) $filter['limit']; + unset($filter['limit']); + } + if (array_key_exists('offset', $filter)) { + $offset = (int) $filter['offset']; + unset($filter['offset']); + } + $select->limit($limit, $offset); + } /** * @@ -246,6 +268,23 @@ { return $this->_sourceData['gso_id_source']; } + + /** + * Returns the recordcount for a given filter + * + * Abstract implementation is not efficient, sources should handle this as efficient + * as possible. + * + * @param array $filter filter array + * @param int $surveyId Gems Survey Id + * @param string $sourceSurveyId Optional Survey Id used by source + * @return int + */ + public function getRawTokenAnswerRowsCount(array $filter, $surveyId, $sourceSurveyId = null) + { + $answers = $this->getRawTokenAnswerRows($filter, $surveyId, $sourceSurveyId); + return count($answers); + } /** * Get the db adapter for this source Modified: trunk/library/classes/Gems/Tracker/Source/SourceInterface.php =================================================================== --- trunk/library/classes/Gems/Tracker/Source/SourceInterface.php 2013-04-10 08:51:22 UTC (rev 1217) +++ trunk/library/classes/Gems/Tracker/Source/SourceInterface.php 2013-04-10 09:59:07 UTC (rev 1218) @@ -184,6 +184,16 @@ * @return array Of nested Field => Value arrays indexed by tokenId */ public function getRawTokenAnswerRows(array $filter, $surveyId, $sourceSurveyId = null); + + /** + * Returns the recordcount for a given filter + * + * @param array $filter filter array + * @param int $surveyId Gems Survey Id + * @param string $sourceSurveyId Optional Survey Id used by source + * @return int + */ + public function getRawTokenAnswerRowsCount(array $filter, $surveyId, $sourceSurveyId = null); /** * Gets the time the survey was started according to the source. Modified: trunk/library/classes/Gems/Tracker/Survey.php =================================================================== --- trunk/library/classes/Gems/Tracker/Survey.php 2013-04-10 08:51:22 UTC (rev 1217) +++ trunk/library/classes/Gems/Tracker/Survey.php 2013-04-10 09:59:07 UTC (rev 1218) @@ -395,6 +395,18 @@ $source = $this->getSource(); return $source->getRawTokenAnswerRows((array) $filter, $this->_surveyId, $this->_gemsSurvey['gsu_surveyor_id']); } + + /** + * Returns the number of answers of multiple tokens + * + * @param array $filter XXX + * @return array Of nested Field => Value arrays indexed by tokenId + */ + public function getRawTokenAnswerRowsCount($filter = array()) + { + $source = $this->getSource(); + return $source->getRawTokenAnswerRowsCount((array) $filter, $this->_surveyId, $this->_gemsSurvey['gsu_surveyor_id']); + } /** * Retrieve the name of the resultfield This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <gem...@li...> - 2013-04-10 10:08:38
|
Revision: 1220 http://sourceforge.net/p/gemstracker/code/1220 Author: mennodekker Date: 2013-04-10 10:08:36 +0000 (Wed, 10 Apr 2013) Log Message: ----------- export: initial survey is empty now and does not show a count minor cleanup in compliancetablesnippet Modified Paths: -------------- trunk/library/classes/Gems/Default/ExportAction.php trunk/library/classes/Gems/Snippets/Tracker/Compliance/ComplianceTableSnippet.php Modified: trunk/library/classes/Gems/Default/ExportAction.php =================================================================== --- trunk/library/classes/Gems/Default/ExportAction.php 2013-04-10 10:02:57 UTC (rev 1219) +++ trunk/library/classes/Gems/Default/ExportAction.php 2013-04-10 10:08:36 UTC (rev 1220) @@ -129,7 +129,7 @@ { $empty = $this->util->getTranslated()->getEmptyDropdownArray(); $tracks = $empty + $this->util->getTrackData()->getSteppedTracks(); - $surveys = $this->util->getDbLookup()->getSurveysForExport(isset($data['tid']) ? $data['tid'] : null); + $surveys = $empty + $this->util->getDbLookup()->getSurveysForExport(isset($data['tid']) ? $data['tid'] : null); $organizations = $this->loader->getCurrentUser()->getRespondentOrganizations(); $types = $this->export->getExportClasses(); @@ -158,7 +158,7 @@ //Add a field to the form showing the record count. If this is too slow for large recordsets //then remove it or make it more efficient unset($data['records']); - if (isset($data['sid'])) { + if (!empty($data['sid'])) { $survey = $this->loader->getTracker()->getSurvey(intval($data['sid'])); $filter = $this->_getFilter($data); //$answers = $survey->getRawTokenAnswerRows($filter); Modified: trunk/library/classes/Gems/Snippets/Tracker/Compliance/ComplianceTableSnippet.php =================================================================== --- trunk/library/classes/Gems/Snippets/Tracker/Compliance/ComplianceTableSnippet.php 2013-04-10 10:02:57 UTC (rev 1219) +++ trunk/library/classes/Gems/Snippets/Tracker/Compliance/ComplianceTableSnippet.php 2013-04-10 10:08:36 UTC (rev 1220) @@ -110,7 +110,6 @@ $cDesc = null; } } - $cIcon = $model->get($name, 'roundIcon'); $class = 'newRound'; $thClass = $class .' ' . $alternateClass; // Add alternate class only for th $th = $th_row->td(array('class' => $thClass)); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <gem...@li...> - 2013-04-11 09:51:30
|
Revision: 1221 http://sourceforge.net/p/gemstracker/code/1221 Author: mennodekker Date: 2013-04-11 09:51:25 +0000 (Thu, 11 Apr 2013) Log Message: ----------- First working version of batch export: no longer timeouts for big excel exports. TODO: spss export + error checking etc. Modified Paths: -------------- trunk/library/classes/Gems/Default/ExportAction.php trunk/library/classes/Gems/Export/Excel.php trunk/library/classes/Gems/Export/ExportAbstract.php trunk/library/classes/Gems/Menu/MenuAbstract.php trunk/library/classes/Gems/Task/TaskRunnerBatch.php Added Paths: ----------- trunk/library/classes/Gems/Export/ExportBatchInterface.php trunk/library/classes/Gems/Task/Export/ trunk/library/classes/Gems/Task/Export/Finalize.php trunk/library/classes/Gems/Task/Export/Step.php Modified: trunk/library/classes/Gems/Default/ExportAction.php =================================================================== --- trunk/library/classes/Gems/Default/ExportAction.php 2013-04-10 10:08:36 UTC (rev 1220) +++ trunk/library/classes/Gems/Default/ExportAction.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -61,6 +61,18 @@ * @var Zend_Locale */ public $locale; + + /** + * + * @var Gems_Util_RequestCache + */ + public $requestCache; + + /** + * + * @var Zend_Session_Namespace + */ + protected $_session; public function __construct(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response, array $invokeArgs = array()) { @@ -69,6 +81,8 @@ //Add this controller to the export so it can render view when needed $this->export->controller = $this; + + $this->_session = GemsEscort::getInstance()->session; } @@ -118,7 +132,40 @@ // Gems_Tracker::$verbose = true; return $filter; } + + /** + * Modify request to hold a cache + * + * @return array + */ + public function getCachedRequestData() + { + if (! $this->requestCache) { + $this->requestCache = $this->util->getRequestCache($this->getRequest()->getActionName(), false); + $this->requestCache->setMenu($this->menu); + $this->requestCache->setRequest($this->getRequest()); + // Button text should not be stored. + $this->requestCache->removeParams('export', 'action'); + } + + $data = $this->requestCache->getProgramParams(); + + // Clean up empty values + // + // We do this here because empty values can be valid filters that overrule the default + foreach ($data as $key => $value) { + if ((is_array($value) && empty($value)) || (is_string($value) && 0 === strlen($value))) { + unset($data[$key]); + } + } + + // Do not set, we only want to have the data as a default + //$this->getRequest()->setParams($data); + + return $data; + } + /** * Retrieve the form * @@ -234,36 +281,117 @@ */ public function handleExport($data) { - $language = $this->locale->getLanguage(); - $survey = $this->loader->getTracker()->getSurvey($data['sid']); - $filter = $this->_getFilter($data); - $answers = $survey->getRawTokenAnswerRows($filter); - $answerModel = $survey->getAnswerModel($language); - - //Now add the organization id => name mapping - $answerModel->set('organizationid', 'multiOptions', $this->loader->getCurrentUser()->getAllowedOrganizations()); - - if (count($answers) === 0) { - $answers[0] = array('' => sprintf($this->_('No %s found.'), $this->getTopic(0))); - } - if (isset($data['type'])) { //Do the logging $message = Zend_Json::encode($data); Gems_AccessLog::getLog()->log('export', $this->getRequest(), $message, null, true); - + //And delegate the export to the right class $exportClass = $this->export->getExport($data['type']); - $exportClass->handleExport($data, $survey, $answers, $answerModel, $language); + + if ($exportClass instanceof Gems_Export_ExportBatchInterface) { + // Clear possible existing batch + $batch = $this->loader->getTaskRunnerBatch('export_data'); + $batch->reset(); + // Have a batch handle the export + $this->_session->exportParams = $data; + $this->_reroute(array('action'=>'handle-export')); + + } else { + // If not possible / available, handle direct + $language = $this->locale->getLanguage(); + $survey = $this->loader->getTracker()->getSurvey($data['sid']); + $filter = $this->_getFilter($data); + $answers = $survey->getRawTokenAnswerRows($filter); + $answerModel = $survey->getAnswerModel($language); + + //Now add the organization id => name mapping + $answerModel->set('organizationid', 'multiOptions', $this->loader->getCurrentUser()->getAllowedOrganizations()); + + if (count($answers) === 0) { + $answers[0] = array('' => sprintf($this->_('No %s found.'), $this->getTopic(0))); + } + + $exportClass->handleExport($data, $survey, $answers, $answerModel, $language); + } } } + + public function handleExportAction() + { + $this->initHtml(); + $batch = $this->loader->getTaskRunnerBatch('export_data'); + $batch->minimalStepDurationMs = 2000; + if (!$batch->count()) { + $data = $this->_session->exportParams; + $filter = $this->_getFilter($data); + $language = $this->locale->getLanguage(); + + $exportClass = $this->export->getExport($data['type']); + $exportClass->handleExportBatch($batch, $filter, $language, $data); + $batch->autoStart = true; + } + + $title = $this->_('Export'); + // Not using batchrunner since we need something else + if ($batch->run($this->getRequest())) { + exit; + } else { + $controller = $this; + $controller->html->h3($title); + if ($batch->isFinished()) { + $messages = $batch->getMessages(); + if (array_key_exists('file', $messages)) { + $files = $messages['file']; + unset($messages['file']); + unset($messages['export-progress']); + } + + $controller->addMessage($messages); + + if (!empty($files)) { + // Forward to download action + $this->_session->exportFile = $files; + $this->_reroute(array('action'=>'download')); + } + } else { + if ($batch->count()) { + $controller->html->append($batch->getPanel($controller->view, $batch->getProgressPercentage() . '%')); + } else { + $controller->html->pInfo($controller->_('Nothing to do.')); + } + $controller->html->pInfo(MUtil_Html_AElement::a(MUtil_Html_UrlArrayAttribute::rerouteUrl($this->getRequest(), array('action'=>'index')), array('class'=>'actionlink'), $this->_('Back'))); + } + } + } + + public function downloadAction() + { + $this->view->layout()->disableLayout(); + $this->_helper->viewRenderer->setNoRender(true); + $files = $this->_session->exportFile; + foreach($files['headers'] as $header) { + header($header); + } + while (ob_get_level()) { + ob_end_clean(); + } + readfile(GEMS_ROOT_DIR . '/var/tmp/' . $files['file']); + // Now clean up the file + unlink(GEMS_ROOT_DIR . '/var/tmp/' . $files['file']); + exit; + } + + public function indexAction() { $this->initHtml(); + + $data = $this->getCachedRequestData(); //Hacked around to get a self-refreshing form, quite hardcoded but fine for now - if ($form = $this->processForm()) { + if ($form = $this->processForm(null, $data)) { if (!$this->getRequest()->isPost() || $form->getElement('export')->isChecked()) { if ($form->getElement('export')->isChecked()) { $data = $form->getValues(); @@ -309,7 +437,7 @@ $data = $request->getPost() + (array) $data; } else { //Set the defaults for the form here - $data = $this->export->getDefaults(); + $data = $data + $this->export->getDefaults(); } $form = $this->getForm($data); Modified: trunk/library/classes/Gems/Export/Excel.php =================================================================== --- trunk/library/classes/Gems/Export/Excel.php 2013-04-10 10:08:36 UTC (rev 1220) +++ trunk/library/classes/Gems/Export/Excel.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -45,7 +45,7 @@ * @license New BSD License * @since Class available since version 1.5 */ -class Gems_Export_Excel extends Gems_Export_ExportAbstract +class Gems_Export_Excel extends Gems_Export_ExportAbstract implements Gems_Export_ExportBatchInterface { /** * Return an array of Form Elements for this specific export @@ -135,4 +135,164 @@ $this->view->setScriptPath(GEMS_LIBRARY_DIR . '/views/scripts'); $this->export->controller->render('excel',null,true); } + + /** + * This method handles the export with the given options + * + * The method takes care of rendering the right script by using $this->export->controller to + * access the controller object. + * + * @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 + * @param array $data The formdata + */ + 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 + $filename = $survey->getName() . '.xls'; + + $files['file']= 'export-' . md5(time() . rand()); + $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 + + $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> +<meta http-equiv=Content-Type content="text/html; charset=UTF-8"> +<meta name=ProgId content=Excel.Sheet> +<meta name=Generator content="Microsoft Excel 11"> +<style> + /* Default styles for tables */ + + table { + border-collapse: collapse; + border: .5pt solid #000000; + } + + tr th { + font-weight: bold; + padding: 3px 8px; + border: .5pt solid #000000; + background: #c0c0c0 + } + tr td { + padding: 3px 8px; + border: .5pt solid #000000; + } + td {mso-number-format:"\@";} +</style> +</head> +<body>'); + + if (isset($data[$this->getName()])) { + $options = $data[$this->getName()]; + if (isset($options['format'])) { + $options = $options['format']; + } + } else { + $options = array(); + } + + $headers = array(); + if (in_array('formatVariable', $options)) { + $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) { + if (isset($questions[$key])) { + $headers[$key] = $questions[$key]; + } else { + $headers[$key] = $key; + } + } + } else { + $headers = array_keys($answers[0]); + } + + //Only for the first row: output headers + $output = "<table>\r\n"; + $output .= "\t<thead>\r\n"; + $output .= "\t\t<tr>\r\n"; + foreach ($headers as $name => $value) { + $output .= "\t\t\t<th>$value</th>\r\n"; + } + $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; + $step = 500; + do { + $filter['limit'] = $step; + $filter['offset'] = $current; + $batch->addTask('Export_Step', $data['type'], $filter, $language, $data); + $current = $current + $step; + } while ($current < $answerCount); + + $batch->addTask('Export_Finalize', $data['type']); + + return; + } + + public function handleExportBatchStep($batch, $data, $filter, $language) + { + $files = $batch->getMessage('file', array()); + $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'])) { + $options = $options['format']; + } + } 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) + { + $output = "\t\t<tr>\r\n"; + foreach ($answer as $key => $value) { + $output .= "\t\t\t<td>$value</td>\r\n"; + } + $output .= "\t\t</tr>\r\n"; + fwrite($f,$output); + } + fclose($f); + } + + public function handleExportBatchFinalize($batch, $data) + { + $files = $batch->getMessage('file', array()); + $batch->setMessage('export-progress', $this->_('Export finished')); + $f = fopen(GEMS_ROOT_DIR . '/var/tmp/' . $files['file'], 'a'); + fwrite($f, ' </tbody> + </table> + </body> +</html>'); + fclose($f); + } + } \ No newline at end of file Modified: trunk/library/classes/Gems/Export/ExportAbstract.php =================================================================== --- trunk/library/classes/Gems/Export/ExportAbstract.php 2013-04-10 10:08:36 UTC (rev 1220) +++ trunk/library/classes/Gems/Export/ExportAbstract.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -55,6 +55,12 @@ * @var Gems_Export */ public $export; + + /** + * + * @var Gems_Loader + */ + public $loader; /** * @var Zend_Translate Copied: trunk/library/classes/Gems/Export/ExportBatchInterface.php (from rev 1210, trunk/library/classes/Gems/Export/ExportInterface.php) =================================================================== --- trunk/library/classes/Gems/Export/ExportBatchInterface.php (rev 0) +++ trunk/library/classes/Gems/Export/ExportBatchInterface.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -0,0 +1,80 @@ +<?php +/** + * Copyright (c) 2011, Erasmus MC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Erasmus MC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @package Gems + * @subpackage Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @version $Id$ + */ + +/** + * The export interface + * + * Exporting survey-data can be done for various sorts of output formats, this interface + * describes the methods needed to implement an output format + * + * @package Gems + * @subpackage Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @since Class available since version 1.6.1 + */ +interface Gems_Export_ExportBatchInterface extends Gems_Export_ExportInterface +{ + /** + * This method handles setting up all needed stept for the batch export + * + * Normally this will initialize the file to download and set up as much + * steps as needed and the final job to close 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 + * @param array $data The formdata + */ + public function handleExportBatch($batch, $filter, $language, $data); + + /** + * Executes a step in exporting the data, this should be as large as possible + * without hitting max request time limits + * + * @param Gems_Task_TaskRunnerBatch $batch The batch to start + * @param array $data The formdata + * @param array $filter The filter to use + * @param string $language The language used / to use for the export + */ + public function handleExportBatchStep($batch, $data, $filter, $language); + + /** + * Final step in batch export, perform cleanup / finalize the file + * + * @param Gems_Task_TaskRunnerBatch $batch + * @param array $data + */ + public function handleExportBatchFinalize($batch, $data); +} \ No newline at end of file Modified: trunk/library/classes/Gems/Menu/MenuAbstract.php =================================================================== --- trunk/library/classes/Gems/Menu/MenuAbstract.php 2013-04-10 10:08:36 UTC (rev 1220) +++ trunk/library/classes/Gems/Menu/MenuAbstract.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -275,6 +275,8 @@ // EXPORT $export->addPage($this->_('Survey answers'), 'pr.export', 'export', 'index'); + $export->addButtonOnly('', 'pr.export', 'export', 'handle-export'); + $export->addButtonOnly('', 'pr.export', 'export', 'download'); // EXPORT TO HTML $export->addPage($this->_('Respondent archives'), 'pr.export-html', 'respondent-export', 'index'); Added: trunk/library/classes/Gems/Task/Export/Finalize.php =================================================================== --- trunk/library/classes/Gems/Task/Export/Finalize.php (rev 0) +++ trunk/library/classes/Gems/Task/Export/Finalize.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -0,0 +1,54 @@ +<?php +/** + * Copyright (c) 2011, Erasmus MC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Erasmus MC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @package Gems + * @subpackage Task_Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @version $Id$ + */ + +/** + * Perform one step of results export + * + * @package Gems + * @subpackage Task_Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @since Class available since version 1.6.1 + */ +class Gems_Task_Export_Finalize extends Gems_Task_TaskAbstract { + + public function execute($exportType = null, $filter = array(), $language = null, $data = array()) + { + // $filter now has a limit and offset + $export = $this->loader->getExport()->getExport($exportType); + $export->handleExportBatchFinalize($this->_batch, $data); + + return; + } +} \ No newline at end of file Added: trunk/library/classes/Gems/Task/Export/Step.php =================================================================== --- trunk/library/classes/Gems/Task/Export/Step.php (rev 0) +++ trunk/library/classes/Gems/Task/Export/Step.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -0,0 +1,54 @@ +<?php +/** + * Copyright (c) 2011, Erasmus MC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Erasmus MC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @package Gems + * @subpackage Task_Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @version $Id$ + */ + +/** + * Perform one step of results export + * + * @package Gems + * @subpackage Task_Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @since Class available since version 1.6.1 + */ +class Gems_Task_Export_Step extends Gems_Task_TaskAbstract { + + public function execute($exportType = null, $filter = array(), $language = null, $data = array()) + { + // $filter now has a limit and offset + $export = $this->loader->getExport()->getExport($exportType); + $export->handleExportBatchStep($this->_batch, $data, $filter, $language); + + return; + } +} \ No newline at end of file Modified: trunk/library/classes/Gems/Task/TaskRunnerBatch.php =================================================================== --- trunk/library/classes/Gems/Task/TaskRunnerBatch.php 2013-04-10 10:08:36 UTC (rev 1220) +++ trunk/library/classes/Gems/Task/TaskRunnerBatch.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -54,6 +54,16 @@ */ public $loader; + /** + * The minimal time used between send progress reports. + * + * This enables quicker processing as multiple steps can be taken in a single + * run(), without the run taking too long to answer. + * + * Set to 0 to report back on each step. + * + * @var int + */ public $minimalStepDurationMs = 1000; public function __construct($id = null) @@ -99,6 +109,23 @@ return $this; } + + /** + * Get a message from the message stack with a specific id. + * + * @param scalar $id + * @param string $default A default message + * @return string + */ + public function getMessage($id, $default = null) + { + $messages = parent::getMessages(); + if (array_key_exists($id, $messages)) { + return $messages[$id]; + } else { + return $default; + } + } public function runTask($task, $params) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <gem...@li...> - 2013-04-15 10:30:30
|
Revision: 1228 http://sourceforge.net/p/gemstracker/code/1228 Author: mennodekker Date: 2013-04-15 10:30:26 +0000 (Mon, 15 Apr 2013) Log Message: ----------- Made batch export a little more generic Modified Paths: -------------- trunk/library/classes/Gems/Export/Excel.php trunk/library/classes/Gems/Export/ExportAbstract.php trunk/library/classes/Gems/Export/ExportBatchInterface.php Added Paths: ----------- trunk/library/classes/Gems/Task/Export/ExportCommand.php Removed Paths: ------------- trunk/library/classes/Gems/Task/Export/Finalize.php trunk/library/classes/Gems/Task/Export/Step.php Modified: trunk/library/classes/Gems/Export/Excel.php =================================================================== --- trunk/library/classes/Gems/Export/Excel.php 2013-04-11 15:43:08 UTC (rev 1227) +++ trunk/library/classes/Gems/Export/Excel.php 2013-04-15 10:30:26 UTC (rev 1228) @@ -139,9 +139,15 @@ /** * This method handles the export with the given options * - * The method takes care of rendering the right script by using $this->export->controller to - * access the controller object. - * + * 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 + * handleExportBatchStep Exports the records + * 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 @@ -238,17 +244,18 @@ do { $filter['limit'] = $step; $filter['offset'] = $current; - $batch->addTask('Export_Step', $data['type'], $filter, $language, $data); + $batch->addTask('Export_ExportCommand', $data['type'], 'handleExportBatchStep', $data, $filter, $language); $current = $current + $step; } while ($current < $answerCount); - $batch->addTask('Export_Finalize', $data['type']); + $batch->addTask('Export_ExportCommand', $data['type'], 'handleExportBatchFinalize'); return; } - public function handleExportBatchStep($batch, $data, $filter, $language) + 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); @@ -283,10 +290,10 @@ fclose($f); } - public function handleExportBatchFinalize($batch, $data) + public function handleExportBatchFinalize() { - $files = $batch->getMessage('file', array()); - $batch->setMessage('export-progress', $this->_('Export finished')); + $files = $this->_batch->getMessage('file', array()); + $this->_batch->setMessage('export-progress', $this->_('Export finished')); $f = fopen(GEMS_ROOT_DIR . '/var/tmp/' . $files['file'], 'a'); fwrite($f, ' </tbody> </table> Modified: trunk/library/classes/Gems/Export/ExportAbstract.php =================================================================== --- trunk/library/classes/Gems/Export/ExportAbstract.php 2013-04-11 15:43:08 UTC (rev 1227) +++ trunk/library/classes/Gems/Export/ExportAbstract.php 2013-04-15 10:30:26 UTC (rev 1228) @@ -35,9 +35,10 @@ */ /** - * Short description for ExportAbstract + * This is a helper class to make implementing exports easier * - * Long description for class ExportAbstract (if any)... + * The setBatch method will only be used when running batch exports. To do so + * you must implement the Gems_Export_ExportBatchInterface * * @package Gems * @subpackage Export @@ -46,8 +47,15 @@ * @since Class available since version 1.5 */ abstract class Gems_Export_ExportAbstract extends Gems_Loader_TargetLoaderAbstract implements Gems_Export_ExportInterface -{ +{ /** + * Holds the current batch if there is any + * + * @var Gems_Task_TaskRunnerBatch + */ + protected $_batch = null; + + /** * Variable needed to access the controller functions * * $this->export->controller @@ -87,4 +95,16 @@ { return $this->translate->getAdapter()->_($text, $locale); } + + /** + * Set the batch to be used by this source + * + * Use $this->hasBatch to check for existence + * + * @param Gems_Task_TaskRunnerBatch $batch + */ + public function setBatch(Gems_Task_TaskRunnerBatch $batch) + { + $this->_batch = $batch; + } } \ No newline at end of file Modified: trunk/library/classes/Gems/Export/ExportBatchInterface.php =================================================================== --- trunk/library/classes/Gems/Export/ExportBatchInterface.php 2013-04-11 15:43:08 UTC (rev 1227) +++ trunk/library/classes/Gems/Export/ExportBatchInterface.php 2013-04-15 10:30:26 UTC (rev 1228) @@ -47,7 +47,7 @@ interface Gems_Export_ExportBatchInterface extends Gems_Export_ExportInterface { /** - * This method handles setting up all needed stept for the batch export + * This method handles setting up all needed steps for the batch export * * Normally this will initialize the file to download and set up as much * steps as needed and the final job to close the file. @@ -58,23 +58,13 @@ * @param array $data The formdata */ public function handleExportBatch($batch, $filter, $language, $data); - + /** - * Executes a step in exporting the data, this should be as large as possible - * without hitting max request time limits - * - * @param Gems_Task_TaskRunnerBatch $batch The batch to start - * @param array $data The formdata - * @param array $filter The filter to use - * @param string $language The language used / to use for the export - */ - public function handleExportBatchStep($batch, $data, $filter, $language); - - /** - * Final step in batch export, perform cleanup / finalize the file - * + * Set the batch to be used by this source + * + * Use $this->hasBatch to check for existence + * * @param Gems_Task_TaskRunnerBatch $batch - * @param array $data */ - public function handleExportBatchFinalize($batch, $data); + public function setBatch(Gems_Task_TaskRunnerBatch $batch); } \ No newline at end of file Added: trunk/library/classes/Gems/Task/Export/ExportCommand.php =================================================================== --- trunk/library/classes/Gems/Task/Export/ExportCommand.php (rev 0) +++ trunk/library/classes/Gems/Task/Export/ExportCommand.php 2013-04-15 10:30:26 UTC (rev 1228) @@ -0,0 +1,58 @@ +<?php +/** + * Copyright (c) 2011, Erasmus MC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Erasmus MC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @package Gems + * @subpackage Task_Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @version $Id$ + */ + +/** + * Executes any command in an Export class for a given $exportType + * + * @package Gems + * @subpackage Task_Tracker + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @since Class available since version 1.5.3 + */ +class Gems_Task_Export_ExportCommand extends Gems_Task_TaskAbstract +{ + public function execute($exportType = null, $command = null) + { + $params = array_slice(func_get_args(), 2); + $export = $this->loader->getExport()->getExport($exportType); + $export->setBatch($this->_batch); + + if ($messages = call_user_func_array(array($export, $command), $params)) { + foreach ($messages as $message) { + $this->_batch->addMessage($command . ': ' . $message); + } + } + } +} \ No newline at end of file Deleted: trunk/library/classes/Gems/Task/Export/Finalize.php =================================================================== --- trunk/library/classes/Gems/Task/Export/Finalize.php 2013-04-11 15:43:08 UTC (rev 1227) +++ trunk/library/classes/Gems/Task/Export/Finalize.php 2013-04-15 10:30:26 UTC (rev 1228) @@ -1,54 +0,0 @@ -<?php -/** - * Copyright (c) 2011, Erasmus MC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Erasmus MC nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @package Gems - * @subpackage Task_Export - * @copyright Copyright (c) 2011 Erasmus MC - * @license New BSD License - * @version $Id$ - */ - -/** - * Perform one step of results export - * - * @package Gems - * @subpackage Task_Export - * @copyright Copyright (c) 2011 Erasmus MC - * @license New BSD License - * @since Class available since version 1.6.1 - */ -class Gems_Task_Export_Finalize extends Gems_Task_TaskAbstract { - - public function execute($exportType = null, $filter = array(), $language = null, $data = array()) - { - // $filter now has a limit and offset - $export = $this->loader->getExport()->getExport($exportType); - $export->handleExportBatchFinalize($this->_batch, $data); - - return; - } -} \ No newline at end of file Deleted: trunk/library/classes/Gems/Task/Export/Step.php =================================================================== --- trunk/library/classes/Gems/Task/Export/Step.php 2013-04-11 15:43:08 UTC (rev 1227) +++ trunk/library/classes/Gems/Task/Export/Step.php 2013-04-15 10:30:26 UTC (rev 1228) @@ -1,54 +0,0 @@ -<?php -/** - * Copyright (c) 2011, Erasmus MC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Erasmus MC nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @package Gems - * @subpackage Task_Export - * @copyright Copyright (c) 2011 Erasmus MC - * @license New BSD License - * @version $Id$ - */ - -/** - * Perform one step of results export - * - * @package Gems - * @subpackage Task_Export - * @copyright Copyright (c) 2011 Erasmus MC - * @license New BSD License - * @since Class available since version 1.6.1 - */ -class Gems_Task_Export_Step extends Gems_Task_TaskAbstract { - - public function execute($exportType = null, $filter = array(), $language = null, $data = array()) - { - // $filter now has a limit and offset - $export = $this->loader->getExport()->getExport($exportType); - $export->handleExportBatchStep($this->_batch, $data, $filter, $language); - - return; - } -} \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
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. |
From: <gem...@li...> - 2013-04-15 12:37:03
|
Revision: 1231 http://sourceforge.net/p/gemstracker/code/1231 Author: mennodekker Date: 2013-04-15 12:37:00 +0000 (Mon, 15 Apr 2013) Log Message: ----------- Minor fixes to export Modified Paths: -------------- trunk/library/classes/Gems/Default/ExportAction.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 12:36:24 UTC (rev 1230) +++ trunk/library/classes/Gems/Default/ExportAction.php 2013-04-15 12:37:00 UTC (rev 1231) @@ -281,7 +281,7 @@ */ public function handleExport($data) { - if (isset($data['type'])) { + if (isset($data['type']) && !empty($data['sid'])) { //Do the logging $message = Zend_Json::encode($data); Gems_AccessLog::getLog()->log('export', $this->getRequest(), $message, null, true); Modified: trunk/library/classes/Gems/Export/Spss.php =================================================================== --- trunk/library/classes/Gems/Export/Spss.php 2013-04-15 12:36:24 UTC (rev 1230) +++ trunk/library/classes/Gems/Export/Spss.php 2013-04-15 12:37:00 UTC (rev 1231) @@ -137,6 +137,22 @@ return $input; } + /** + * This method handles setting up all needed steps for the batch export + * + * 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 + * @param string $language The language used / to use for the export + * @param array $data The formdata + */ public function handleExportBatch($batch, $filter, $language, $data) { $survey = $this->loader->getTracker()->getSurvey($data['sid']); @@ -144,8 +160,8 @@ $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); + // don't export empty data + return; } else { $answers = reset($answers); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <gem...@li...> - 2013-04-17 15:47:03
|
Revision: 1236 http://sourceforge.net/p/gemstracker/code/1236 Author: matijsdejong Date: 2013-04-17 15:46:57 +0000 (Wed, 17 Apr 2013) Log Message: ----------- Robustness improvements with extra checks on field lengths and input validation Modified Paths: -------------- trunk/library/classes/Gems/Tracker/RespondentTrack.php trunk/library/classes/Gems/Tracker/Source/LimeSurvey1m9Database.php trunk/library/classes/Gems/Tracker/Token.php trunk/library/classes/Gems/User/UserLoader.php Modified: trunk/library/classes/Gems/Tracker/RespondentTrack.php =================================================================== --- trunk/library/classes/Gems/Tracker/RespondentTrack.php 2013-04-16 10:12:11 UTC (rev 1235) +++ trunk/library/classes/Gems/Tracker/RespondentTrack.php 2013-04-17 15:46:57 UTC (rev 1236) @@ -371,6 +371,11 @@ $this->_activeTokens[$token->getRoundId()] = $token; } + // Nothing to find + if (! $roundId) { + return null; + } + // Use array_key_exists since there may not be a valid round if (! array_key_exists($roundId, $this->_activeTokens)) { $tokenSelect = $this->tracker->getTokenSelect(); Modified: trunk/library/classes/Gems/Tracker/Source/LimeSurvey1m9Database.php =================================================================== --- trunk/library/classes/Gems/Tracker/Source/LimeSurvey1m9Database.php 2013-04-16 10:12:11 UTC (rev 1235) +++ trunk/library/classes/Gems/Tracker/Source/LimeSurvey1m9Database.php 2013-04-17 15:46:57 UTC (rev 1236) @@ -91,7 +91,14 @@ private $_languageMap; /** + * The default text length attribute fields should have. * + * @var int + */ + protected $attributeSize = 255; + + /** + * * @var Zend_Locale */ protected $locale; @@ -130,21 +137,25 @@ { $missingFields = array(); - $lengths = array(); - if (preg_match('/\(([^\)]+)\)/', $tokenTable['token']['Type'], $lengths)) { - $tokenLength = $lengths[1]; - } else { - $tokenLength = 0; - } + $tokenLength = $this->_extractFieldLength($tokenTable['token']['Type']); $token_library = $this->tracker->getTokenLibrary(); if ($tokenLength < $token_library->getLength()) { $tokenLength = $token_library->getLength(); - $missingFields['token'] = "CHANGE COLUMN `token` `token` varchar($tokenLength) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NULL"; + $missingFields['token'] = 'CHANGE COLUMN `token` `token` varchar(' . $tokenLength . + ") CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NULL"; } foreach ($this->_attributeMap as $name => $field) { if (! isset($tokenTable[$field])) { - $missingFields[$field] = "ADD $field varchar(255) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'"; + $missingFields[$field] = 'ADD ' . $field . ' varchar(' . $this->attributeSize . + ") CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'"; + } else { + $attrLength = $this->_extractFieldLength($tokenTable[$field]['Type']); + if ($attrLength < $this->attributeSize) { + $missingFields[$field] = "CHANGE COLUMN `$field` `$attrLength` varchar(" . + $this->attributeSize . + ") CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NULL"; + } } } @@ -152,6 +163,21 @@ } /** + * + * @param string $typeDescr E.g. int(11) or varchar(36) + * @return int In case 11 or 36 + */ + private function _extractFieldLength($typeDescr) + { + $lengths = array(); + if (preg_match('/\(([^\)]+)\)/', $typeDescr, $lengths)) { + return $lengths[1]; + } + + return 0; + } + + /** * Returns a list of field names that should be set in a newly inserted token. * * @param Gems_Tracker_Token $token @@ -159,10 +185,14 @@ */ protected function _fillAttributeMap(Gems_Tracker_Token $token) { - $values[$this->_attributeMap['respondentid']] = (string) $token->getRespondentId(); - $values[$this->_attributeMap['organizationid']] = (string) $token->getOrganizationId(); - $values[$this->_attributeMap['consentcode']] = (string) $token->getConsentCode(); - $values[$this->_attributeMap['resptrackid']] = (string) $token->getRespondentTrackId(); + $values[$this->_attributeMap['respondentid']] = + substr($token->getRespondentId(), 0, $this->attributeSize); + $values[$this->_attributeMap['organizationid']] = + substr($token->getOrganizationId(), 0, $this->attributeSize); + $values[$this->_attributeMap['consentcode']] = + substr($token->getConsentCode(), 0, $this->attributeSize); + $values[$this->_attributeMap['resptrackid']] = + substr($token->getRespondentTrackId(), 0, $this->attributeSize); return $values; } @@ -381,6 +411,7 @@ // SELECT sid, surveyls_title AS short_title, surveyls_description AS description, active, datestamp, ' . $this->_anonymizedField . ' $select = $lsDb->select(); + // 'alloweditaftercompletion' ? $select->from($this->_getSurveysTableName(), array('active', 'datestamp', 'tokenanswerspersistence', $this->_anonymizedField)) ->joinInner( $this->_getSurveyLanguagesTableName(), @@ -829,7 +860,7 @@ $sourceSurveyId = $this->_getSid($surveyId); } $select = $this->getRawTokenAnswerRowsSelect($filter, $surveyId, $sourceSurveyId); - + //Now process the filters $lsSurveyTable = $this->_getSurveyTableName($sourceSurveyId); $tokenField = $lsSurveyTable . '.token'; @@ -870,10 +901,10 @@ return array(); } - + /** * Returns the recordcount for a given filter - * + * * @param array $filter filter array * @param int $surveyId Gems Survey Id * @param string $sourceSurveyId Optional Survey Id used by source @@ -881,16 +912,16 @@ */ public function getRawTokenAnswerRowsCount(array $filter, $surveyId, $sourceSurveyId = null) { $select = $this->getRawTokenAnswerRowsSelect($filter, $surveyId, $sourceSurveyId); - + $p = new Zend_Paginator_Adapter_DbSelect($select); $count = $p->getCountSelect()->query()->fetchColumn(); - + return $count; } - + /** * Get the select object to use for RawTokenAnswerRows - * + * * @param array $filter * @param type $surveyId * @param type $sourceSurveyId @@ -937,7 +968,7 @@ // Add limit / offset to select and remove from filter $this->filterLimitOffset($filter, $select); - + foreach ($filter as $field => $values) { $field = $lsDb->quoteIdentifier($field); if (is_array($values)) { Modified: trunk/library/classes/Gems/Tracker/Token.php =================================================================== --- trunk/library/classes/Gems/Tracker/Token.php 2013-04-16 10:12:11 UTC (rev 1235) +++ trunk/library/classes/Gems/Tracker/Token.php 2013-04-17 15:46:57 UTC (rev 1236) @@ -125,7 +125,22 @@ protected $locale; /** + * The size of the result field, calculated from meta data when null, + * but can be set by project specific class to fixed value * + * @var int The maximum character length of the result field + */ + protected $resultFieldLength = null; + + /** + * Cache for storing the calculation of the length + * + * @var int the character length of the result field + */ + protected static $staticResultFieldLength = null; + + /** + * * @var Gems_Tracker_Survey */ protected $survey; @@ -213,6 +228,29 @@ } /** + * The maximum length of the result field + * + * @return int + */ + protected function _getResultFieldLength() + { + if (null !== $this->resultFieldLength) { + return $this->resultFieldLength; + } + + if (null !== self::$staticResultFieldLength) { + $this->resultFieldLength = self::$staticResultFieldLength; + return $this->resultFieldLength; + } + + $model = new MUtil_Model_TableModel('gems__tokens'); + self::$staticResultFieldLength = $model->get('gto_result', 'maxlength'); + $this->resultFieldLength = self::$staticResultFieldLength; + + return $this->resultFieldLength; + } + + /** * Update the token, both in the database and in memory. * * @param array $values The values that this token should be set to @@ -500,6 +538,11 @@ // not casting to strings means e.g. float results always result in // an update, even when they did not change. $values['gto_result'] = (string) $rawAnswers[$resultField]; + + // Chunk of text that is too long + if ($len = $this->_getResultFieldLength()) { + $values['gto_result'] = substr($values['gto_result'], 0, $len); + } } } } Modified: trunk/library/classes/Gems/User/UserLoader.php =================================================================== --- trunk/library/classes/Gems/User/UserLoader.php 2013-04-16 10:12:11 UTC (rev 1235) +++ trunk/library/classes/Gems/User/UserLoader.php 2013-04-17 15:46:57 UTC (rev 1236) @@ -518,7 +518,7 @@ return $this->loadUser(self::USER_PROJECT, $organization, $login_name); } - if ((null == $login_name) || (null == $organization)) { + if ((null == $login_name) || (null == $organization) || (! intval($organization))) { return $this->loadUser(self::USER_NOLOGIN, $organization, $login_name); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <gem...@li...> - 2013-04-17 16:29:42
|
Revision: 1237 http://sourceforge.net/p/gemstracker/code/1237 Author: matijsdejong Date: 2013-04-17 16:29:37 +0000 (Wed, 17 Apr 2013) Log Message: ----------- Bug: Nologin user was staff Added LimeSurvey 2.00 source type Modified Paths: -------------- trunk/library/classes/Gems/Tracker.php trunk/library/classes/Gems/User/NoLoginDefinition.php Added Paths: ----------- trunk/library/classes/Gems/Tracker/Source/LimeSurvey2m00Database.php Copied: trunk/library/classes/Gems/Tracker/Source/LimeSurvey2m00Database.php (from rev 1235, trunk/library/classes/Gems/Tracker/Source/LimeSurvey1m91Database.php) =================================================================== --- trunk/library/classes/Gems/Tracker/Source/LimeSurvey2m00Database.php (rev 0) +++ trunk/library/classes/Gems/Tracker/Source/LimeSurvey2m00Database.php 2013-04-17 16:29:37 UTC (rev 1237) @@ -0,0 +1,80 @@ +<?php + +/** + * Copyright (c) 2011, Erasmus MC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Erasmus MC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * @package Gems + * @subpackage Tracker + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @version $Id$ + */ + +/** + * Class description of LimeSurvey1m91Database + * + * Difference with 1.9 version: + * - private field was renamed to anonymized + * + * @package Gems + * @subpackage Tracker + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @since Class available since version 1.6.1 + */ +class Gems_Tracker_Source_LimeSurvey2m00Database extends Gems_Tracker_Source_LimeSurvey1m91Database +{ + /** + * Returns a list of field names that should be set in a newly inserted token. + * + * Adds the fields without default new in 2.00 + * + * @param Gems_Tracker_Token $token + * @return array Of fieldname => value type + */ + protected function _fillAttributeMap(Gems_Tracker_Token $token) + { + $values = parent::_fillAttributeMap($token); + + return self::addnewAttributeDefaults($values); + } + + /** + * Adds the fields without default new in 2.00 + * + * @param Gems_Tracker_Token $token + * @return array Of fieldname => value type + */ + public static function addnewAttributeDefaults(array $values) + { + // Not really attributes, but htey need a value + $values['participant_id'] = ''; + $values['blacklisted'] = ''; + + return $values; + } +} \ No newline at end of file Modified: trunk/library/classes/Gems/Tracker.php =================================================================== --- trunk/library/classes/Gems/Tracker.php 2013-04-17 15:46:57 UTC (rev 1236) +++ trunk/library/classes/Gems/Tracker.php 2013-04-17 16:29:37 UTC (rev 1237) @@ -75,6 +75,7 @@ protected $_sourceClasses = array( 'LimeSurvey1m9Database' => 'Lime Survey 1.90 DB', 'LimeSurvey1m91Database' => 'Lime Survey 1.91+ DB', + 'LimeSurvey2m00Database' => 'Lime Survey 2.00 DB', ); /** @@ -337,7 +338,7 @@ { return $this->getTokenLibrary()->filter($tokenId); } - + /** * * @param mixed $respTrackData Track id or array containing trackdata @@ -360,9 +361,9 @@ /** * Get all tracks for a respondent - * + * * Specify the optional $order to sort other than on start date - * + * * @param int $userId * @param int $organizationId * @param mixed $order The column(s) and direction to order by Modified: trunk/library/classes/Gems/User/NoLoginDefinition.php =================================================================== --- trunk/library/classes/Gems/User/NoLoginDefinition.php 2013-04-17 15:46:57 UTC (rev 1236) +++ trunk/library/classes/Gems/User/NoLoginDefinition.php 2013-04-17 16:29:37 UTC (rev 1237) @@ -75,4 +75,16 @@ 'user_role' => 'nologin', ); } + + /** + * Returns true when users using this definition are staff members. + * + * Used only when the definition does not return a user_staff field. + * + * @return boolean + */ + public function isStaff() + { + return false; + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |