def _validate_field_names(self, fieldnames, filename): if not self._case_sensitive_col_names: fieldnames = [field.lower() for field in fieldnames] if self.FLD_LINE_NUM in fieldnames: raise ttrk.BadFormatError( "An invalid field name ({:}) was found im {:} - this is a reserved field name" .format(self.FLD_LINE_NUM, filename)) for field in self._fields: if field not in fieldnames and not self._fields[field]['optional']: raise ttrk.BadFormatError( "Mandatory field '{:} is missing in file {:}".format( field, filename))
def update_text_target_for_trial(exp_info, trial, use_numeric_target_as_default=False): """ Update properties of the text stimuli according to the current trial info :param exp_info: :type exp_info: trajtracker.paradigms.common.BaseExperimentInfo :param trial: :type trial: trajtracker.paradigms.common.BaseTrialInfo :param use_numeric_target_as_default: For number-to-position paradigm: if this is set to True, the text.target column in the CSV input file becomes optional, and the "target" column is used as the default text to show. """ if not trial.use_text_targets: # No text in this trial exp_info.text_target.texts = [] return if "text.target" in trial.csv_data: # -- Set the text to show as target (or several, semicolon-separated texts, in case of RSVP) trial.text_target = trial.csv_data["text.target"] elif use_numeric_target_as_default and 'target' in dir(trial): trial.text_target = str(trial.target) else: raise ttrk.BadFormatError("The input CSV file does not contain a 'text.target' column!") exp_info.text_target.texts = trial.text_target.split(";") update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.font', 'text_font') update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.text_size', 'text_size') update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.bold', 'text_bold') update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.italic', 'text_italic') update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.underline', 'text_underline') update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.justification', 'text_justification') update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.text_colour', 'text_colour') update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.background_colour', 'background_colour') update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.size', 'size') update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.onset_time', 'onset_time') update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.duration', 'duration') upd_xy = update_attr_by_csv_config(exp_info, trial, exp_info.text_target, 'text.position', 'position') upd_x = _update_target_stimulus_position(exp_info, trial, exp_info.text_target, 'text', 'x') upd_y = _update_target_stimulus_position(exp_info, trial, exp_info.text_target, 'text', 'y') #-- Save stimulus coordinates in output file if upd_xy or upd_x or upd_y: exp_info.exported_trial_result_fields['text.position'] = None trial.results['text.position'] = format_coord_to_csv(exp_info.text_target.position)
def _transform_types(self, row, filename): for field in row: if field not in self._fields: continue field_type = self._fields[field]['type'] if field_type != str: try: row[field] = field_type(row[field]) except BaseException as e: raise ttrk.BadFormatError( "Invalid CSV file format (line {:} in {:}): {:}". format(row[self.FLD_LINE_NUM], filename, e)) return row
def create_feedback_stimuli(exp_info): """ Create the stimuli to be used as feedback for the participant's response :param exp_info: :type exp_info: trajtrackerp.dchoice.ExperimentInfo """ common.validate_config_param_values("feedback_stim_type", exp_info.config.feedback_stim_type, ['rectangle', 'picture', None]) common.validate_config_param_values("feedback_select_by", exp_info.config.feedback_select_by, ['response', 'expected', 'accuracy']) common.validate_config_param_type("feedback_duration", numbers.Number, exp_info.config.feedback_duration) if exp_info.config.feedback_select_by in ('accuracy', 'expected') and \ len(exp_info.trials) > 0 and 'expected_response' not in exp_info.trials[0]: raise ttrk.BadFormatError( 'Invalid format for {:}: when config.feedback_select_by=accuracy, you must include an expected_response column in the file' .format(exp_info.config.data_source)) fb_type = exp_info.config.feedback_stim_type if fb_type == 'picture': _create_feedback_pictures(exp_info) elif fb_type == 'rectangle': _create_feedback_rectangles(exp_info) positions = _get_feedback_stim_positions(exp_info) for i in range(len(exp_info.feedback_stimuli)): feedback_stim = exp_info.feedback_stimuli[i] feedback_stim.position = positions[i] feedback_stim.visible = False #-- Hide feedback stimuli after delay exp_info.event_manager.register_operation( ttrk.events.TRIAL_SUCCEEDED + exp_info.config.feedback_duration, lambda t1, t2: hide_feedback_stimuli(exp_info), recurring=True, cancel_pending_operation_on=ttrk.events.TRIAL_STARTED, description="Hide feedback stimulus")
def load_from_csv(self, filename, id_type=str): """ Load trajectories from a CSV file. The file should have the following columns: - x, y: the coordinates - time: a time point of these coordinates - visible: whether the stimulus should be visible in this time point - traj_id: use this column to specify several trajectories in a single file. If this column is missing, the class assumes that there is only one trajectory in the file, and its ID will be 1. The file should be sorter properly: all lines of a single trajectory should be grouped together, and times should appear in ascending order. :param filename: Name of the file (full path) :param id_type: Convert the traj_id column in the file from str to this type """ _u.validate_func_arg_type(self, "load_from_csv", "filename", filename, str) fp, reader = self._open_and_get_reader(filename) has_traj_id_col = 'traj_id' in reader.fieldnames has_visible_col = 'visible' in reader.fieldnames if len(self._trajectories) > 0 and not has_traj_id_col: raise trajtracker.BadFormatError(("Invalid file format in {:}.load_from_csv('{:}'): " + "there is no traj_id column in the file").format(type(self).__name__, filename)) loaded_traj_ids = set() try: #-- Validate file format for col_name in ['x', 'y', 'time']: if col_name not in reader.fieldnames: raise trajtracker.BadFormatError( "Invalid file format in {:}.load_from_csv('{:}'): there is no '{:}' column".format( type(self).__name__, filename, col_name)) prev_traj_id = None traj_data = [] for row in reader: traj_id = id_type(row['traj_id']) if has_traj_id_col else 1 if traj_id in loaded_traj_ids: raise trajtracker.BadFormatError( "Invalid file format in {:}.load_from_csv('{:}'): the lines of trajectory '{:}' are not consecutive".format( type(self).__name__, filename, traj_id)) if prev_traj_id is None: # First trajectory in the file prev_traj_id = traj_id elif prev_traj_id is not None and prev_traj_id != traj_id: #-- A new trajectory is starting; save the trajectory that has just ended self.set_trajectory(prev_traj_id, traj_data) loaded_traj_ids.add(prev_traj_id) traj_data = [] prev_traj_id = traj_id #-- Save current line as a time point time = float(row['time']) x = int(row['x']) y = int(row['y']) if has_visible_col: visible = not (row['visible'] == '0' or row['visible'].lower().startswith('f')) timepoint = (time, x, y, visible) else: timepoint = (time, x, y) traj_data.append(timepoint) if len(traj_data) > 0: self.set_trajectory(prev_traj_id, traj_data) finally: fp.close()