Stair one button.py

From PsyPad
Jump to: navigation, search
#
# Process single button staircase log files from PsyPad to produce
# "readable" output.
#
# Andrew Turpin
# Fri 21 Mar 2014 10:53:51 EST
#

import re
import json
import time
import datetime
import sys

global rev_values    # list of [list of reversal values] per staircase
global mins          # list of min dB value per staircase
global maxs          # list of max dB value per staircase
global seen_max      # count of number of times max dB seen per staircase
global not_seen_min  # count of number of times min dB not seen per staircase
global hits_to_finish # number of times min dB not seen or max dB seen per staircase
global start_time    # time of first question
global end_time      # time of test end

####################################################################################
# Print a line for a single presentation
# If caused a reversal add a *
# If it is a min or max value, 
#   record in seen_max or not_seen_min if appropriate
# INPUTS
#   date         - date in seconds since epoc
#   reversal     - True or False
#   currentStair - number of this staircase
#   stim_level   - stimulus level presented
#   button       - True if button pressed
# OUTPUTS:
#   Nothing
# SIDEFFECTS:
#   Prints line of text describing presentation
####################################################################################
def printIt(date, reversal, currentStair, stim_level, button):
    if reversal:
        rev_values[currentStair] = rev_values[currentStair] + [stim_level]
        flag = "*"
    else:
        flag = ""
         
    if button == -1:
        but = " Not seen"
    else:
        but = str(button)
    t = time.strftime("%a, %d %b %Y %H:%M:%S",time.localtime(float(date)))
    print "# " + t + " stair= " + str(currentStair) + " stim= " + str(stim_level) + " button= " + but + flag

    if button and stim_level == maxs[currentStair]:
        seen_max[currentStair] = seen_max[currentStair] + 1

    if not button and stim_level == mins[currentStair]:
        not_seen_min[currentStair] = not_seen_min[currentStair] + 1

################################################################################
# Get information from JSON header in log 
# and initialise global variables accordingly
# INPUTS:
#   inData - string of data that is in 3rd field of 1st line of logfiles
# OUTPUTS:
#   Nothing
# SIDEFFECTS:
#   Prints max and min stim values
################################################################################
def extract_test_info(inData):
    global rev_values, mins, maxs, seen_max, not_seen_min, hits_to_finish 

    d = json.loads(inData)
    
    if d['use_staircase_method'] != 1:
        print "This script only works on staircases, not MOCS"
        sys.exit()
    
    mins = [ 36-int(x[-2:]) for x in d['staircase_max_level'].split("/")]  # note db inverts image name
    maxs = [ 36-int(x[-2:]) for x in d['staircase_min_level'].split("/")]  # note db inverts image name
    rev_values   = [[] for i in range(d['number_of_staircases']) ]  # set up a list of empty lists, one per staircase
    seen_max     = [0 for i in range(d['number_of_staircases']) ]  # set up a list of empty lists, one per staircase
    not_seen_min = [0 for i in range(d['number_of_staircases']) ]  # set up a list of empty lists, one per staircase
    hits_to_finish = [ int(x) for x in d['hits_to_finish'].split("/")]
    
    print "# Maxs: {0}".format(maxs)
    print "# Mins: {0}".format(mins)
    
################################################################################
# Process log to get and print information about each presentation
# INPUTS:
#   f - file handle
# OUTPUTS:
#   None
# SIDEFFECTS:
#   Prints stuff
################################################################################
def get_presentations(f):
    global start_time, end_time

    firstTime = True

    for line in f:
        fields = line.split("|")    # each line has 3 parts
        date = fields[0]            # date in first part
        type = fields[1]            # type/code in second part
        inData = fields[2]          # inData in third part

        if type == "test_begin":
            extract_test_info(inData)

        if type == "currentStaircase":
            currentStair = int(inData.strip())
        
        if type == "reversal":
            reversal = True
        
        if type == "next_question":
            if firstTime:
                firstTime = False
                start_time = date
            else:
                printIt(date, reversal, currentStair, stim_level, button)
            reversal = False
            button = -1
        
        if type == "presented_image":
            A = inData.split("/")       # inData is of format q/b_xxx
            stim_level = int(A[0])      # sitm level is the last two chars before / (qq)
            corr_button = A[1][0]       # button that should be pressed is first char (b) (most likely 1)
        
        if type == "button_press":
            button = inData[0] == corr_button   # button number is first char of inData
    
        if type == "test_finished":
            end_time = date

        # print final presentation once all lines read
    printIt(date, reversal, currentStair, stim_level, button)

################################################################################
# Open file and process each line
################################################################################
def main():
    global start_time, end_time

    if len(sys.argv) != 2:
        print("Usage: python {0} logfile_name".format(sys.argv[0]))
        sys.exit(1)

    f = open(sys.argv[1])

    get_presentations(f)

    #####################################
    # print the means of rev_values
    #####################################
    for s in range(len(rev_values)):
        if seen_max[s] == hits_to_finish[s]:
            print "Staircase {0}: Maxed    {1} Seen     {2} times".format(s, maxs[s], seen_max[s])
    
        if not_seen_min[s] == hits_to_finish[s]:
            print "Staircase {0}: Minned   {1} Not_seen {2} times".format(s, mins[s], not_seen_min[s])
    
        if len(rev_values[s]) > 0:
            print "Staircase {0}: Mean_rev {1} Last_rev {2}".format(s, sum(rev_values[s])/len(rev_values[s]), rev_values[s][-1])

    #####################################
    # print the time elapsed
    #####################################
    st = time.localtime(float(start_time))
    et = time.localtime(float(end_time))
    print "Start time " + time.strftime("%a, %d %b %Y %H:%M:%S",st)
    print "End   time " + time.strftime("%a, %d %b %Y %H:%M:%S",et)
    print "Elapsed: " + str(datetime.timedelta(seconds=(int(end_time) - int(start_time))))

if __name__ == '__main__':
    main()