<commit_before>#!/usr/local/bin/python

import os
import csv
import copy
import datetime as dt
from pyoperant.behavior import base, shape
from pyoperant.errors import EndSession
from pyoperant import components, utils, reinf, queues

class TwoAltChoiceExp(base.BaseExp):
    """A two alternative choice experiment

    Parameters
    ----------


    Attributes
    ----------
    req_panel_attr : list
        list of the panel attributes that are required for this behavior
    fields_to_save : list
        list of the fields of the Trial object that will be saved
    trials : list
        all of the trials that have run
    shaper : Shaper
        the protocol for shaping 
    parameters : dict 
        all additional parameters for the experiment
    data_csv : string 
        path to csv file to save data
    reinf_sched : object
        does logic on reinforcement



    """
    def __init__(self, *args, **kwargs):
        super(TwoAltChoiceExp,  self).__init__(*args, **kwargs)
        self.shaper = shape.Shaper2AC(self.panel, self.log, self.parameters, self.log_error_callback)

        # # assign stim files full names
        for name, filename in self.parameters['stims'].items():
            filename_full = os.path.join(self.parameters['stim_path'], filename)
            self.parameters['stims'][name] = filename_full

        self.req_panel_attr += ['speaker',
                                'left',
                                'center',
                                'right',
                                'reward',
                                'punish',
                                ]

        # configure csv file for data
        self.fields_to_save = ['session',
                               'index',
                               'type_',
                               'stimulus',
                               'class_',
                               'response',
                               'correct',
                               'rt',
                               'reward',
                               'punish',
                               'time',
                               ]

        if 'add_fields_to_save' in self.parameters.keys():
            self.fields_to_save += self.parameters['add_fields_to_save']

        self.trials = []
        self.session_id = 0
        self.trial_q = None
        self.session_q = None

        self.data_csv = os.path.join(self.parameters['experiment_path'],
                                     self.parameters['subject']+'_trialdata_'+self.timestamp+'.csv')
        self.make_data_csv()

        if 'reinforcement' in self.parameters.keys():
            reinforcement = self.parameters['reinforcement']
            if reinforcement['schedule'] == 'variable_ratio':
                self.reinf_sched = reinf.VariableRatioSchedule(ratio=reinforcement['ratio'])
            elif reinf['schedule'] == 'fixed_ratio':
                self.reinf_sched = reinf.FixedRatioSchedule(ratio=reinforcement['ratio'])
            else:
                self.reinf_sched = reinf.ContinuousReinforcement()

        else:
            self.reinf_sched = reinf.ContinuousReinforcement()

        if 'block_design' not in self.parameters:
            self.parameters['block_design'] = {
                'blocks': {
                    'default': {
                        'queue': 'random',
                        'conditions': [{'class': k} for k in self.parameters['classes'].keys()]
                        }
                    },
                'order': ['default']
                }

    def make_data_csv(self):
        """ Create the csv file to save trial data

        This creates a new csv file at experiment.data_csv and writes a header row 
        with the fields in experiment.fields_to_save
        """
        with open(self.data_csv, 'wb') as data_fh:
            trialWriter = csv.writer(data_fh)
            trialWriter.writerow(self.fields_to_save)

    ## session flow
    def check_session_schedule(self):
        """ Check the session schedule

        Returns
        -------
        bool
            True if sessions should be running
        """
        return self.check_light_schedule()

    def session_pre(self):
        """ Runs before the session starts

        For each stimulus class, if there is a component associated with it, that
        component is mapped onto `experiment.class_assoc[class]`. For example, 
        if the `left` port is registered with the 'L' class, you can access the response 
        port through `experiment.class_assoc['L']`.

        """
        self.class_assoc = {}
        for class_, class_params in self.parameters['classes'].items():
            try:
                self.class_assoc[class_] = getattr(self.panel,class_params['component'])
            except KeyError:
                pass

        return 'main'

    def session_main(self):
        """ Runs the sessions

        Inside of `session_main`, we loop through sessions and through the trials within
        them. This relies heavily on the 'block_design' parameter, which controls trial
        conditions and the selection of queues to generate trial conditions.

        """

        if self.session_q is None:
            self.session_q = queues.block_queue(self.parameters['block_design']['order'])

        for sn_cond in self.session_q:

            self.trials = []
            self.do_correction = False
            self.session_id += 1
            self.log.info('starting session %s: %s' % (self.session_id,sn_cond))

            if self.trial_q is None:
                # grab the block details
                blk = copy.deepcopy(self.parameters['block_design']['blocks'][sn_cond])

                # load the block details into the trial queue
                q_type = blk.pop('queue')
                if q_type=='random':
                    self.trial_q = queues.random_queue(**blk)
                elif q_type=='block':
                    self.trial_q = queues.block_queue(**blk)
                elif q_type=='staircase':
                    self.trial_q = queues.staircase_queue(self,**blk)

            for tr_cond in self.trial_q:
                try:
                    self.new_trial(tr_cond)
                    self.run_trial()
                    while self.do_correction:
                        self.new_trial(tr_cond)
                        self.run_trial()
                except EndSession:
                    return 'post'
            self.trial_q = None

        self.session_q = None

        return 'post'

    def session_post(self):
        """ Closes out the sessions

        """
        self.log.info('ending session')
        return None

    ## trial flow
    def new_trial(self,conditions=None):
        """Creates a new trial and appends it to the trial list

        If `self.do_correction` is `True`, then the conditions are ignored and a new<commit_msg>fixed handling of trials left in queue<commit_after>#!/usr/local/bin/python

import os
import csv
import copy
import datetime as dt
from pyoperant.behavior import base, shape
from pyoperant.errors import EndSession
from pyoperant import components, utils, reinf, queues

class TwoAltChoiceExp(base.BaseExp):
    """A two alternative choice experiment

    Parameters
    ----------


    Attributes
    ----------
    req_panel_attr : list
        list of the panel attributes that are required for this behavior
    fields_to_save : list
        list of the fields of the Trial object that will be saved
    trials : list
        all of the trials that have run
    shaper : Shaper
        the protocol for shaping 
    parameters : dict 
        all additional parameters for the experiment
    data_csv : string 
        path to csv file to save data
    reinf_sched : object
        does logic on reinforcement



    """
    def __init__(self, *args, **kwargs):
        super(TwoAltChoiceExp,  self).__init__(*args, **kwargs)
        self.shaper = shape.Shaper2AC(self.panel, self.log, self.parameters, self.log_error_callback)

        # # assign stim files full names
        for name, filename in self.parameters['stims'].items():
            filename_full = os.path.join(self.parameters['stim_path'], filename)
            self.parameters['stims'][name] = filename_full

        self.req_panel_attr += ['speaker',
                                'left',
                                'center',
                                'right',
                                'reward',
                                'punish',
                                ]

        # configure csv file for data
        self.fields_to_save = ['session',
                               'index',
                               'type_',
                               'stimulus',
                               'class_',
                               'response',
                               'correct',
                               'rt',
                               'reward',
                               'punish',
                               'time',
                               ]

        if 'add_fields_to_save' in self.parameters.keys():
            self.fields_to_save += self.parameters['add_fields_to_save']

        self.trials = []
        self.session_id = 0
        self.trial_q = None
        self.session_q = None

        self.data_csv = os.path.join(self.parameters['experiment_path'],
                                     self.parameters['subject']+'_trialdata_'+self.timestamp+'.csv')
        self.make_data_csv()

        if 'reinforcement' in self.parameters.keys():
            reinforcement = self.parameters['reinforcement']
            if reinforcement['schedule'] == 'variable_ratio':
                self.reinf_sched = reinf.VariableRatioSchedule(ratio=reinforcement['ratio'])
            elif reinf['schedule'] == 'fixed_ratio':
                self.reinf_sched = reinf.FixedRatioSchedule(ratio=reinforcement['ratio'])
            else:
                self.reinf_sched = reinf.ContinuousReinforcement()

        else:
            self.reinf_sched = reinf.ContinuousReinforcement()

        if 'block_design' not in self.parameters:
            self.parameters['block_design'] = {
                'blocks': {
                    'default': {
                        'queue': 'random',
                        'conditions': [{'class': k} for k in self.parameters['classes'].keys()]
                        }
                    },
                'order': ['default']
                }

    def make_data_csv(self):
        """ Create the csv file to save trial data

        This creates a new csv file at experiment.data_csv and writes a header row 
        with the fields in experiment.fields_to_save
        """
        with open(self.data_csv, 'wb') as data_fh:
            trialWriter = csv.writer(data_fh)
            trialWriter.writerow(self.fields_to_save)

    ## session flow
    def check_session_schedule(self):
        """ Check the session schedule

        Returns
        -------
        bool
            True if sessions should be running
        """
        return self.check_light_schedule()

    def session_pre(self):
        """ Runs before the session starts

        For each stimulus class, if there is a component associated with it, that
        component is mapped onto `experiment.class_assoc[class]`. For example, 
        if the `left` port is registered with the 'L' class, you can access the response 
        port through `experiment.class_assoc['L']`.

        """
        self.class_assoc = {}
        for class_, class_params in self.parameters['classes'].items():
            try:
                self.class_assoc[class_] = getattr(self.panel,class_params['component'])
            except KeyError:
                pass

        return 'main'

    def session_main(self):
        """ Runs the sessions

        Inside of `session_main`, we loop through sessions and through the trials within
        them. This relies heavily on the 'block_design' parameter, which controls trial
        conditions and the selection of queues to generate trial conditions.

        """

        def run_trial_queue():
            for tr_cond in self.trial_q:
                self.new_trial(tr_cond)
                self.run_trial()
                while self.do_correction:
                    self.new_trial(tr_cond)
                    self.run_trial()
            self.trial_q = None

        if self.session_q is None:
            self.log.info('Next sessions: %s' % self.parameters['block_design']['order'])
            self.session_q = queues.block_queue(self.parameters['block_design']['order'])

        if self.trial_q is None:
            for sn_cond in self.session_q:

                self.trials = []
                self.do_correction = False
                self.session_id += 1
                self.log.info('starting session %s: %s' % (self.session_id,sn_cond))

                # grab the block details
                blk = copy.deepcopy(self.parameters['block_design']['blocks'][sn_cond])

                # load the block details into the trial queue
                q_type = blk.pop('queue')
                if q_type=='random':
                    self.trial_q = queues.random_queue(**blk)
                elif q_type=='block':
                    self.trial_q = queues.block_queue(**blk)
                elif q_type=='staircase':
                    self.trial_q = queues.staircase_queue(self,**blk)

                try: 
                    run_trial_queue()
                except EndSession:
                    return 'post'

            self.session_q = None
        
        else:
            self.log.info('continuing last session')
            try: 
                run_trial_queue()
            except EndSession:
                return 'post'

        return 'post'

    def session_post(self):
        """ Closes out the sessions

        """
        self.log.info('ending session')
        return None

    ## trial flow
    def new_trial(self,conditions=None):
        """Creates a new trial and appends it to the trial list

        If `self.do_correction` is `True`, then the conditions are ignored and a new
{'fraction_non_alphanumeric': 0.09109439797568004, 'fraction_numerical': 0.0004217333239614817, 'mean_word_length': 1.572875226039783, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3381929', 'n_tokens_mistral': 3767, 'n_tokens_neox': 3491, 'n_words': 964}