def __report_incomplete(self, participant_ids): log_path = os.path.join(P.local_dir, "uninitialized_users_{0}".format(now(True))) log = io.open(log_path, "w+", encoding='utf-8') p_cols = [ 'user_id', 'session_structure', 'session_count', 'sessions_completed', 'figure_set', 'handedness', 'created' ] header = p_cols + ["session_rows", "trial_rows"] log.write("\t".join(header)) for p in participant_ids: data = self.db_select('participants', p_cols, where={'id': p[0]})[0] num_sessions = len( self.db_select('sessions', ['id'], where={'participant_id': p[0]})) num_trials = len( self.db_select('trials', ['id'], where={'participant_id': p[0]})) data += [num_sessions, num_trials] log.write("\n") log.write("\t".join([utf8(i) for i in data])) log.close() self.exp.quit()
def clean_up(self): if self.session_number == self.session_count and P.enable_learned_figures_querying: self.fig_dir = os.path.join(self.p_dir, "learned") if not os.path.exists(self.fig_dir): os.makedirs(self.fig_dir) learned_fig_num = 1 if query(user_queries.experimental[3]) == "y": self.origin_pos = (P.screen_c[0], int(P.screen_y * 0.8)) self.origin_boundary = [self.origin_pos, P.origin_size // 2] while True: self.setup_response_collector() self.rc.draw_listener.add_boundaries([ ('start', self.origin_boundary, CIRCLE_BOUNDARY), ('stop', self.origin_boundary, CIRCLE_BOUNDARY) ]) self.start_trial_button() self.capture_learned_figure(learned_fig_num) if query(user_queries.experimental[4]) == "y": learned_fig_num += 1 else: break # if the entire experiment is successfully completed, update the sessions_completed column q_str = "UPDATE `participants` SET `sessions_completed` = ? WHERE `id` = ?" self.db.query(q_str, QUERY_UPD, q_vars=[self.session_number, P.participant_id]) # log session data to database session_data = { 'participant_id': P.participant_id, 'user_id': self.user_id, 'session_number': self.session_number, 'completed': now(True) } self.db.insert(session_data, "sessions") # show 'experiment complete' message before exiting experiment msg = message(P.experiment_complete_message, "instructions", blit_txt=False) flush() fill() blit(msg, registration=5, location=P.screen_c) flip() any_key()
def start(self, trial_number): """Starts recording simulated eye events from the mouse cursor. Called automatically at the start of each trial unless ``P.manual_eyelink_recording`` is True, in which case it must be called manually in order to start recording eye events and gaze position from the eye tracker. To stop recording after this method is called, use the :meth:`stop` method. Args: trial_number (int): The current trial number. Used to mark the start of the trial in the data files of eye trackers that support data markup. """ self.local_start_time = now() self.__recording = True self.tracker_start_time = self.now() return 0
def file_name(self): file_name_data = [ P.participant_id, P.block_number, P.trial_number, now(True, "%Y-%m-%d"), self.session_number ] return "p{0}_s{4}_b{1}_t{2}_{3}".format(*file_name_data)
def collect_demographics(anonymous=False): '''Collects participant demographics and writes them to the 'participants' table in the experiment's database, based on the queries in the "demographic" section of the project's user_queries.json file. If P.manual_demographics_collection = True, this function should be called at some point during the setup() section of your experiment class. Otherwise, this function will be run automatically when the experiment is launched. Args: anonymous (bool, optional): If True, this function will log all of the anonymous values for the experiment's demographic queries to the database immediately without prompting the user for input. ''' from klibs.KLEnvironment import exp, db # ie. demographic questions aren't being asked for this experiment if not P.collect_demographics and not anonymous: return # first insert required, automatically-populated fields demographics = EntryTemplate('participants') demographics.log('created', now(True)) try: # columns moved to session_info in newer templates demographics.log("random_seed", P.random_seed) demographics.log("klibs_commit", P.klibs_commit) except ValueError: pass # collect a response and handle errors for each question for q in user_queries.demographic: if q.active: # if querying unique identifier, make sure it doesn't already exist in db if q.database_field == P.unique_identifier: # TODO: fix this to work with multi-user mode existing = db.query("SELECT `{0}` FROM `participants`".format( q.database_field)) while True: value = query(q, anonymous=anonymous) if utf8(value) in [utf8(val[0]) for val in existing]: err = ("A participant with that ID already exists!\n" "Please try a different identifier.") fill() blit( message(err, "alert", align='center', blit_txt=False), 5, P.screen_c) flip() any_key() else: break else: value = query(q, anonymous=anonymous) demographics.log(q.database_field, value) # typical use; P.collect_demographics is True and called automatically by klibs if not P.demographics_collected: P.participant_id = db.insert(demographics) P.p_id = P.participant_id P.demographics_collected = True # Log info about current runtime environment to database if 'session_info' in db.table_schemas.keys(): runtime_info = EntryTemplate('session_info') for col, value in runtime_info_init().items(): runtime_info.log(col, value) db.insert(runtime_info) # Save copy of experiment.py and config files as they were for participant if not P.development_mode: pid = P.random_seed if P.multi_user else P.participant_id # pid set at end for multiuser P.version_dir = join(P.versions_dir, "p{0}_{1}".format(pid, now(True))) os.mkdir(P.version_dir) copyfile("experiment.py", join(P.version_dir, "experiment.py")) copytree(P.config_dir, join(P.version_dir, "Config")) else: # The context for this is: collect_demographics is set to false but then explicitly called later db.update(demographics.table, demographics.defined) if P.multi_session_project and not P.manual_demographics_collection: try: exp.init_session() except: pass