classes/Gems/Export/SpssExport.php
<?php
/**
*
* @package Gems
* @subpackage Export
* @copyright Copyright (c) 2015 Erasmus MC
* @license New BSD License
*/
namespace Gems\Export;
/**
*
* @package Gems
* @subpackage Export
* @copyright Copyright (c) 2015 Erasmus MC
* @license New BSD License
* @since Class available since version 1.7.1
*/
class SpssExport extends ExportAbstract
{
/**
* 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;
/**
* Delimiter used for the DAT export
* @var string
*/
protected $delimiter = ',';
/**
* @var string Current used file extension
*/
protected $fileExtension = '.dat';
/**
* @var array Array with the filter options that should be used for this exporter
*/
protected $modelFilterAttributes = array('formatFunction', 'dateFormat', 'storageFormat', 'itemDisplay');
/**
* Add the help snippet
*
* @return string
*/
public function getHelpSnippet()
{
return 'Export\\ExportInformationSpss';
}
/**
* @return string name of the specific export
*/
public function getName() {
return 'SPSS Export';
}
public function addFooter($filename, $modelId = null, $data = null)
{
parent::addFooter($filename, $modelId, $data);
if ($model = $this->getModel()) {
$this->addSpssFile($filename);
}
}
/**
* Add headers to a specific file
* @param string $filename The temporary filename while the file is being written
*/
protected function addHeader($filename)
{
$file = fopen($filename, 'w');
$bom = pack("CCC", 0xef, 0xbb, 0xbf);
fwrite($file, $bom);
fclose($file);
}
/**
* Add a separate row to a file
* @param array $row a row in the model
* @param file $file The already opened file
*/
public function addRow($row, $file)
{
$exportRow = $this->filterRow($row);
$labeledCols = $this->getLabeledColumns();
$exportRow = array_replace(array_flip($labeledCols), $exportRow);
$changed = false;
foreach ($exportRow as $name => $value) {
$type = $this->model->get($name, 'type');
// When numeric, there could be a non numeric answer, just ignore empty values
if ($type == \MUtil_Model::TYPE_NUMERIC && !empty($value) && !is_numeric($value)) {
$this->model->set($name, 'type', \MUtil_Model::TYPE_STRING);
$changed = true;
}
if ($type == \MUtil_Model::TYPE_STRING) {
$size = (int) $this->model->get($name, 'maxlength');
if (mb_strlen($value)>$size) {
$this->model->set($name, 'maxlength', mb_strlen($value));
$changed = true;
}
}
}
fputcsv($file, $exportRow, $this->delimiter, "'");
if ($changed) {
$modelData = [];
foreach ($exportRow as $name => $value) {
$modelData[$name] = [
'type' => $this->model->get($name, 'type'),
'maxlength' => $this->model->get($name, 'maxlength')
];
}
if ($this->batch) {
$models = $this->batch->getSessionVariable('modelsExtra');
$models[$this->modelId] = $modelData;
$this->batch->setSessionVariable('modelsExtra', $models);
} else {
$models = $this->_session->modelsExtra;
$models[$this->modelId] = $modelData;
$this->_session->modelsExtra = $models;
}
}
}
/**
* Creates a correct SPSS file and adds it to the Files array
*/
protected function addSpssFile($filename)
{
$model = $this->model;
$files = $this->getFiles();
$datFileName = array_search($filename, $files);
$spsFileName = substr($datFileName, 0, -strlen($this->fileExtension)) . '.sps';
$tmpFileName = substr($filename, 0, -strlen($this->fileExtension)) . '.sps';
$this->files[$spsFileName] = $tmpFileName;
if ($this->batch) {
$this->batch->setSessionVariable('files', $this->files);
} else {
$this->_session->files = $this->files;
}
$this->addHeader($tmpFileName);
$file = fopen($tmpFileName, 'a');
//first output our script
fwrite($file,
"SET UNICODE=ON.\n" .
"SHOW LOCALE.\n" .
"PRESERVE LOCALE.\n" .
"SET LOCALE='en_UK'.\n\n" .
"GET DATA\n" .
" /TYPE=TXT\n" .
" /FILE=\"" . $datFileName . "\"\n" .
" /DELCASE=LINE\n" .
" /DELIMITERS=\"".$this->delimiter."\"\n" .
" /QUALIFIER=\"'\"\n" .
" /ARRANGEMENT=DELIMITED\n" .
" /FIRSTCASE=1\n" .
" /IMPORTCASE=ALL\n" .
" /VARIABLES=");
$labeledCols = $this->getLabeledColumns();
$labels = array();
$types = array();
$fixedNames = array();
//$questions = $survey->getQuestionList($language);
foreach ($labeledCols as $colname) {
$fixedNames[$colname] = $this->fixName($colname);
$options = array();
$type = $model->get($colname, 'type');
switch ($type) {
case \MUtil_Model::TYPE_DATE:
$type = 'SDATE10';
break;
case \MUtil_Model::TYPE_DATETIME:
$type = 'DATETIME23';
break;
case \MUtil_Model::TYPE_TIME:
$type = 'TIME8.0';
break;
case \MUtil_Model::TYPE_NUMERIC:
$defaultSize = $this->defaultNumericSize;
$type = 'F';
break;
//When no type set... assume string
case \MUtil_Model::TYPE_STRING:
default:
$defaultSize = $this->defaultAlphaSize;
$type = 'A';
break;
}
$types[$colname] = $type;
if ($type == 'A' || $type == 'F') {
$size = $model->get($colname, 'maxlength'); // This comes from db when available
if (is_null($size)) {
$size = $model->get($colname, 'size'); // This is the display width
if (is_null($size)) {
$size = $defaultSize; // We just don't know, make it the default
}
}
if ($type == 'A') {
$type = $type . $size;
} else {
$type = $type . $size . '.' . ($size - 1); //decimal
}
}
//if (isset($questions[$colname])) {
// $labels[$colname] = $questions[$colname];
//}
fwrite($file, "\n " . $fixedNames[$colname] . ' ' . $type);
}
fwrite($file, ".\nCACHE.\nEXECUTE.\n");
fwrite($file, "\n*Define variable labels.\n");
foreach ($labeledCols as $colname) {
$label = "'" . $this->formatString($model->get($colname, 'label')) . "'";
fwrite($file, "VARIABLE LABELS " . $fixedNames[$colname] . " " . $label . "." . "\n");
}
fwrite($file, "\n*Define value labels.\n");
foreach ($labeledCols as $colname) {
if ($options = $model->get($colname, 'multiOptions')) {
fwrite($file, 'VALUE LABELS ' . $fixedNames[$colname]);
foreach ($options as $option => $label) {
$label = "'" . $this->formatString($label) . "'";
if ($option !== "") {
if ($types[$colname] == 'F') {
//Numeric
fwrite($file, "\n" . $option . ' ' . $label);
} else {
//String
fwrite($file, "\n" . '"' . $option . '" ' . $label);
}
}
}
fwrite($file, ".\n\n");
}
}
fwrite($file, "RESTORE LOCALE.\n");
fclose($file);
}
/**
* 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;
}
/**
* 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)
{
if (is_array($input)) {
$input = join(', ', $input);
}
$output = strip_tags($input);
$output = str_replace(array("'", "\r", "\n"), array("''", ' ', ' '), $output);
//$output = "'" . $output . "'";
return $output;
}
/**
* Preprocess the model to add specific options
*/
protected function preprocessModel()
{
parent::preprocessModel();
$labeledCols = $this->getLabeledColumns();
foreach($labeledCols as $columnName) {
$options = array();
$type = $this->model->get($columnName, 'type');
switch ($type) {
case \MUtil_Model::TYPE_DATE:
$options['dateFormat'] = 'yyyy-MM-dd';
break;
case \MUtil_Model::TYPE_DATETIME:
$options['dateFormat'] = 'dd-MM-yyyy HH:mm:ss';
break;
case \MUtil_Model::TYPE_TIME:
$options['dateFormat'] = 'HH:mm:ss';
break;
case \MUtil_Model::TYPE_NUMERIC:
break;
//When no type set... assume string
case \MUtil_Model::TYPE_STRING:
default:
$type = \MUtil_Model::TYPE_STRING;
$options['formatFunction'] = 'formatString';
break;
}
$options['type'] = $type;
$this->model->set($columnName, $options);
}
// Load extra data
if ($this->batch) {
$models = $this->batch->getSessionVariable('modelsExtra');
} else {
$models = $this->_session->modelsExtra;
}
if (!is_array($models) || !isset($models[$this->modelId])) {
$models[$this->modelId] = [];
}
$modelData = $models[$this->modelId];
foreach($modelData as $name => $items)
{
$this->model->set($name, $items);
}
// Save extra data
if ($this->batch) {
$this->batch->setSessionVariable('modelsExtra', $models);
} else {
$this->_session->modelsExtra = $models;
}
}
}