def openOutputFile(fileName=None, append=False, fileCollisionMethod='rename', encoding='utf-8-sig'): """Open an output file (or standard output) for writing. :Parameters: fileName : None, 'stdout', or str The desired output file name. If `None` or `stdout`, return `sys.stdout`. Any other string will be considered a filename. append : bool, optional If ``True``, append data to an existing file; otherwise, overwrite it with new data. Defaults to ``True``, i.e. appending. fileCollisionMethod : string, optional How to handle filename collisions. Valid values are `'rename'`, `'overwrite'`, and `'fail'`. This parameter is ignored if ``append`` is set to ``True``. Defaults to `rename`. encoding : string, optional The encoding to use when writing the file. This parameter will be ignored if `append` is `False` and `fileName` ends with `.psydat` or `.npy` (i.e. if a binary file is to be written). Defaults to ``'utf-8'``. :Returns: f : file A writable file handle. """ fileName = pathToString(fileName) if (fileName is None) or (fileName == 'stdout'): return sys.stdout if append: mode = 'a' else: if fileName.endswith(('.psydat', '.npy')): mode = 'wb' else: mode = 'w' # Rename the output file if a file of that name already exists # and it should not be appended. if os.path.exists(fileName) and not append: fileName = handleFileCollision( fileName, fileCollisionMethod=fileCollisionMethod) # Do not use encoding when writing a binary file. if 'b' in mode: encoding = None if os.path.exists(fileName) and mode in ['w', 'wb']: logging.warning('Data file %s will be overwritten!' % fileName) # The file wil always be opened in binary writing mode, # see https://docs.python.org/2/library/codecs.html#codecs.open f = codecs.open(fileName, mode=mode, encoding=encoding) return f
def test_handleFileCollision_rename_file_does_not_exist(): temp_dir = mkdtemp() # Create temporary file and close it (to destroy it). We simply use this # procedure to grab a unique file name. with NamedTemporaryFile(dir=temp_dir) as f: path = f.name handled_path = handleFileCollision(fileName=path, fileCollisionMethod='rename') assert path == handled_path
def test_handleFileCollision_rename_file_does_not_exist(): temp_dir = mkdtemp() # Create temporary file and close it (to destroy it). We simply use this # procedure to grab a unique file name. with NamedTemporaryFile(dir=temp_dir) as f: path = f.name handled_path = handleFileCollision(fileName=path, fileCollisionMethod='rename') assert path == handled_path
def test_handleFileCollision_rename_file_exists(): temp_dir = mkdtemp() # Create temporary file and close it (to destroy it). We simply use this # procedure to grab a unique file name. with NamedTemporaryFile(dir=temp_dir, suffix='.xyz') as f: path = f.name handled_path = handleFileCollision(fileName=path, fileCollisionMethod='rename') filename, suffix = os.path.splitext(path) expected_path = '%s_1%s' % (filename, suffix) assert handled_path == expected_path os.rmdir(temp_dir)
def test_handleFileCollision_rename_file_exists(): temp_dir = mkdtemp() # Create temporary file and close it (to destroy it). We simply use this # procedure to grab a unique file name. with NamedTemporaryFile(dir=temp_dir, suffix='.xyz') as f: path = f.name handled_path = handleFileCollision(fileName=path, fileCollisionMethod='rename') filename, suffix = os.path.splitext(path) expected_path = '%s_1%s' % (filename, suffix) assert handled_path == expected_path os.rmdir(temp_dir)
def test_handleFileCollision_rename_multiple_files_exists(): temp_dir = mkdtemp() path = os.path.join(temp_dir, 'test.txt') filename, suffix = os.path.splitext(path) # Create a file to start with. with open(path, 'w') as f: f.write('foo') # Now handle collisions of files with the same name. for i, _ in enumerate(range(10), start=1): handled_path = handleFileCollision(fileName=path, fileCollisionMethod='rename') expected_path = '%s_%i%s' % (filename, i, suffix) assert handled_path == expected_path # We need to write something to the file to create it. with open(handled_path, 'w') as f: f.write('foo') shutil.rmtree(temp_dir)
def test_handleFileCollision_rename_multiple_files_exists(): temp_dir = mkdtemp() path = os.path.join(temp_dir, 'test.txt') filename, suffix = os.path.splitext(path) # Create a file to start with. with open(path, 'w') as f: f.write('foo') # Now handle collisions of files with the same name. for i, _ in enumerate(range(10), start=1): handled_path = handleFileCollision(fileName=path, fileCollisionMethod='rename') expected_path = '%s_%i%s' % (filename, i, suffix) assert handled_path == expected_path # We need to write something to the file to create it. with open(handled_path, 'w') as f: f.write('foo') shutil.rmtree(temp_dir)
def test_handleFileCollision_fail(): _, path = mkstemp() with pytest.raises(IOError): handleFileCollision(fileName=path, fileCollisionMethod='fail')
def test_handleFileCollision_overwrite(): _, path = mkstemp() handled_path = handleFileCollision(fileName=path, fileCollisionMethod='overwrite') assert path == handled_path
def test_handleFileCollision_fail(): _, path = mkstemp() with pytest.raises(IOError): handleFileCollision(fileName=path, fileCollisionMethod='fail')
def saveAsExcel(self, fileName, sheetName='rawData', stimOut=None, dataOut=('n', 'all_mean', 'all_std', 'all_raw'), matrixOnly=False, appendFile=True, fileCollisionMethod='rename'): """ Save a summary data file in Excel OpenXML format workbook (:term:`xlsx`) for processing in most spreadsheet packages. This format is compatible with versions of Excel (2007 or greater) and and with OpenOffice (>=3.0). It has the advantage over the simpler text files (see :func:`TrialHandler.saveAsText()` ) that data can be stored in multiple named sheets within the file. So you could have a single file named after your experiment and then have one worksheet for each participant. Or you could have one file for each participant and then multiple sheets for repeated sessions etc. The file extension `.xlsx` will be added if not given already. :Parameters: fileName: string the name of the file to create or append. Can include relative or absolute path sheetName: string the name of the worksheet within the file stimOut: list of strings the attributes of the trial characteristics to be output. To use this you need to have provided a list of dictionaries specifying to trialList parameter of the TrialHandler and give here the names of strings specifying entries in that dictionary dataOut: list of strings specifying the dataType and the analysis to be performed, in the form `dataType_analysis`. The data can be any of the types that you added using trialHandler.data.add() and the analysis can be either 'raw' or most things in the numpy library, including 'mean','std','median','max','min'. e.g. `rt_max` will give a column of max reaction times across the trials assuming that `rt` values have been stored. The default values will output the raw, mean and std of all datatypes found. appendFile: True or False If False any existing file with this name will be overwritten. If True then a new worksheet will be appended. If a worksheet already exists with that name a number will be added to make it unique. fileCollisionMethod: string Collision method passed to :func:`~psychopy.tools.fileerrortools.handleFileCollision` This is ignored if ``append`` is ``True``. """ if stimOut is None: stimOut = [] if self.thisTrialN < 1 and self.thisRepN < 1: # if both are < 1 we haven't started if self.autoLog: logging.info('TrialHandler.saveAsExcel called but no ' 'trials completed. Nothing saved') return -1 # NB this was based on the limited documentation (1 page wiki) for # openpyxl v1.0 if not haveOpenpyxl: raise ImportError('openpyxl is required for saving files in' ' Excel (xlsx) format, but was not found.') # return -1 # create the data array to be sent to the Excel file dataArray = self._createOutputArray(stimOut=stimOut, dataOut=dataOut, matrixOnly=matrixOnly) if not fileName.endswith('.xlsx'): fileName += '.xlsx' # create or load the file if appendFile and os.path.isfile(fileName): wb = load_workbook(fileName) newWorkbook = False else: if not appendFile: # the file exists but we're not appending, will be overwritten fileName = handleFileCollision(fileName, fileCollisionMethod) wb = Workbook() # create new workbook wb.properties.creator = 'PsychoPy' + psychopy.__version__ newWorkbook = True if newWorkbook: ws = wb.worksheets[0] ws.title = sheetName else: ws = wb.create_sheet() ws.title = sheetName # loop through lines in the data matrix for lineN, line in enumerate(dataArray): if line is None: continue for colN, entry in enumerate(line): if entry is None: entry = '' try: # if it can convert to a number (from numpy) then do it val = float(entry) except Exception: val = str(entry) _cell = _getExcelCellName(col=colN, row=lineN) ws.cell(_cell).value = val wb.save(filename=fileName)
def openOutputFile(fileName, append=False, delim=None, fileCollisionMethod='rename', encoding='utf-8'): """Open an output file (or standard output) for writing. :Parameters: fileName : string The desired output file name. append : bool, optional If ``True``, append data to an existing file; otherwise, overwrite it with new data. Defaults to ``True``, i.e. appending. delim : string, optional The delimiting character(s) between values. For a CSV file, this would be a comma. For a TSV file, it would be ``\t``. Defaults to ``None``. fileCollisionMethod : string, optional How to handle filename collisions. This is ignored if ``append`` is set to ``True``. Defaults to `rename`. encoding : string, optional The encoding to use when writing the file. This parameter will be ignored if `append` is `False` and `fileName` ends with `.psydat` or `.npy` (i.e. if a binary file is to be written). Defaults to ``'utf-8'``. :Returns: f : file A writable file handle. :Notes: If no known filename extension is given, and the delimiter is a comma, the extension ``.csv`` will be chosen automatically. If the extension is unknown and the delimiter is a tab, the extension will be ``.tsv``. ``.txt`` will be chosen otherwise. """ if fileName == 'stdout': f = sys.stdout return f if delim is None: genDelimiter(fileName) if not fileName.endswith(('.dlm', '.DLM', '.tsv', '.TSV', '.txt', '.TXT', '.csv', '.CSV', '.psydat', '.npy', '.json')): if delim == ',': fileName += '.csv' elif delim == '\t': fileName += '.tsv' else: fileName += '.txt' if append: writeFormat = 'a' else: if fileName.endswith(('.psydat', '.npy')): writeFormat = 'wb' else: writeFormat = 'w' # Rename the output file if a file of that name already exists # and it should not be appended. if os.path.exists(fileName) and not append: fileName = handleFileCollision( fileName, fileCollisionMethod=fileCollisionMethod ) # Do not use encoding when writing a binary file. if 'b' in writeFormat: encoding = None if os.path.exists(fileName) and writeFormat in ['w', 'wb']: logging.warning('Data file, %s will be overwritten!' % fileName) f = codecs.open(fileName, writeFormat, encoding=encoding) return f
def test_handleFileCollision_invalid_method(): _, path = mkstemp() with pytest.raises(ValueError): handleFileCollision(fileName=path, fileCollisionMethod='invalid_value')
def test_handleFileCollision_invalid_method(): _, path = mkstemp() with pytest.raises(ValueError): handleFileCollision(fileName=path, fileCollisionMethod='invalid_value')
def openOutputFile(fileName, append=False, delim=None, fileCollisionMethod='rename', encoding='utf-8'): """Open an output file (or standard output) for writing. :Parameters: fileName : string The desired output file name. append : bool, optional If ``True``, append data to an existing file; otherwise, overwrite it with new data. Defaults to ``True``, i.e. appending. delim : string, optional The delimiting character(s) between values. For a CSV file, this would be a comma. For a TSV file, it would be ``\t``. Defaults to ``None``. fileCollisionMethod : string, optional How to handle filename collisions. This is ignored if ``append`` is set to ``True``. Defaults to `rename`. encoding : string, optional The encoding to use when writing the file. This parameter will be ignored if `append` is `False` and `fileName` ends with `.psydat` or `.npy` (i.e. if a binary file is to be written). Defaults to ``'utf-8'``. :Returns: f : file A writable file handle. :Notes: If no known filename extension is given, and the delimiter is a comma, the extension ``.csv`` will be chosen automatically. If the extension is unknown and the delimiter is a tab, the extension will be ``.tsv``. ``.txt`` will be chosen otherwise. """ if fileName == 'stdout': f = sys.stdout return f if delim is None: genDelimiter(fileName) if not fileName.endswith(('.dlm', '.DLM', '.tsv', '.TSV', '.txt', '.TXT', '.csv', '.CSV', '.psydat', '.npy', '.json')): if delim == ',': fileName += '.csv' elif delim == '\t': fileName += '.tsv' else: fileName += '.txt' if append: writeFormat = 'a' else: if fileName.endswith(('.psydat', '.npy')): writeFormat = 'wb' else: writeFormat = 'w' # Rename the output file if a file of that name already exists # and it should not be appended. if os.path.exists(fileName) and not append: fileName = handleFileCollision( fileName, fileCollisionMethod=fileCollisionMethod ) # Do not use encoding when writing a binary file. if 'b' in writeFormat: encoding = None if os.path.exists(fileName) and writeFormat in ['w', 'wb']: logging.warning('Data file, %s will be overwritten!' % fileName) f = codecs.open(fileName, writeFormat, encoding=encoding) return f
def saveAsExcel(self, fileName, sheetName='rawData', stimOut=None, dataOut=('n', 'all_mean', 'all_std', 'all_raw'), matrixOnly=False, appendFile=True, fileCollisionMethod='rename'): """ Save a summary data file in Excel OpenXML format workbook (:term:`xlsx`) for processing in most spreadsheet packages. This format is compatible with versions of Excel (2007 or greater) and and with OpenOffice (>=3.0). It has the advantage over the simpler text files (see :func:`TrialHandler.saveAsText()` ) that data can be stored in multiple named sheets within the file. So you could have a single file named after your experiment and then have one worksheet for each participant. Or you could have one file for each participant and then multiple sheets for repeated sessions etc. The file extension `.xlsx` will be added if not given already. :Parameters: fileName: string the name of the file to create or append. Can include relative or absolute path sheetName: string the name of the worksheet within the file stimOut: list of strings the attributes of the trial characteristics to be output. To use this you need to have provided a list of dictionaries specifying to trialList parameter of the TrialHandler and give here the names of strings specifying entries in that dictionary dataOut: list of strings specifying the dataType and the analysis to be performed, in the form `dataType_analysis`. The data can be any of the types that you added using trialHandler.data.add() and the analysis can be either 'raw' or most things in the numpy library, including 'mean','std','median','max','min'. e.g. `rt_max` will give a column of max reaction times across the trials assuming that `rt` values have been stored. The default values will output the raw, mean and std of all datatypes found. appendFile: True or False If False any existing file with this name will be overwritten. If True then a new worksheet will be appended. If a worksheet already exists with that name a number will be added to make it unique. fileCollisionMethod: string Collision method passed to :func:`~psychopy.tools.fileerrortools.handleFileCollision` This is ignored if ``append`` is ``True``. """ fileName = pathToString(fileName) if stimOut is None: stimOut = [] if self.thisTrialN < 1 and self.thisRepN < 1: # if both are < 1 we haven't started if self.autoLog: logging.info('TrialHandler.saveAsExcel called but no ' 'trials completed. Nothing saved') return -1 # NB this was based on the limited documentation (1 page wiki) for # openpyxl v1.0 if not haveOpenpyxl: raise ImportError('openpyxl is required for saving files in' ' Excel (xlsx) format, but was not found.') # return -1 # create the data array to be sent to the Excel file dataArray = self._createOutputArray(stimOut=stimOut, dataOut=dataOut, matrixOnly=matrixOnly) if not fileName.endswith('.xlsx'): fileName += '.xlsx' # create or load the file if appendFile and os.path.isfile(fileName): wb = load_workbook(fileName) newWorkbook = False else: if not appendFile: # the file exists but we're not appending, will be overwritten fileName = handleFileCollision(fileName, fileCollisionMethod) wb = Workbook() # create new workbook wb.properties.creator = 'PsychoPy' + psychopy.__version__ newWorkbook = True if newWorkbook: ws = wb.worksheets[0] ws.title = sheetName else: ws = wb.create_sheet() ws.title = sheetName # loop through lines in the data matrix for lineN, line in enumerate(dataArray): if line is None: continue for colN, entry in enumerate(line): if entry is None: entry = '' try: # if it can convert to a number (from numpy) then do it val = float(entry) except Exception: val = u"{}".format(entry) ws.cell(column=colN + 1, row=lineN + 1, value=val) wb.save(filename=fileName)
def test_handleFileCollision_overwrite(): _, path = mkstemp() handled_path = handleFileCollision(fileName=path, fileCollisionMethod='overwrite') assert path == handled_path