Experiment Builder checksum-validation and automatic data source selection

How to implement checksum validation based on session name and automatic selection of data source based on session name in Experiment Builder (Eye-tracking software from SR Research).

How to grab sequential data-source list items based on the entered session name

It is possible to select a row in a trial data-source based on the entered session name (.edf name). An example experiment builder file is provided here (example.edb), showing an implementation of this technique. (It also shows python-checksum-validation, see below). 

In brief:
A variable node is added with the label LISTNUM_TO_RUN, which is updated with an "update attribute action" node with these settings:

Attribute:
@LISTNUM_TO_RUN.value@
Value:
=@parent.sessionName@[2:5]

Note that in this example the characters from position 2 to 5 are selected to be used for selecting the matching data-row. E.g. a session name of NO1010 would select the data-row with the value 101.

In the TRIAL sequence, a first conditional checks if the iteration is greater than 20.  Since we only have 12 data-rows, if the iteration is greater than 20 we know we've tried to find a data-row and failed to find a viable trial through all of the data source entries. The number would of course need to be adjusted based on the data source to be used -- you can just put a very high number in. If the conditional detects iterations beyond 20, we show a display screen stating as such and terminate the experiment so that it can be re-run with a valid number.

The second conditional checks the data source's "ListNum" column to see if the upcoming trial has the same value as the previously saved variable (the characters from position 2 to 5 of the session name).  If so, we run the trial.  If not, we recycle the trial and send it to the end of the list.  

How to implement (Damm) checksum-validation based on the entered session name

An example experiment builder file is provided here (example.edb), showing this technique implemented. (It also shows data source selection, see above). This requires that valid session names are pre-generated with the same algorithm (Python code for this is provided in the bottom of this document).

  1. Go to edit > preferences > experiment > "enable custom class"
  2. Add a new Custom Class Instance to the experiment (found in the "Other"-tab)
  3. Double-click the custom class instance, and replace all code with the provided python code (found below)
  4. Put =@parent.sessionName@ into property1 of the custom class instance attributes.
  5. The provided Python code (below) will check if an entered session name/id is a Damm-valid id. It will return 1 if valid, and 0 otherwise. It is possible to filter the session id prior to validation: E.g. in the provided python code, the characters "NO" are removed from the session id prior to Damm validation. To incorporate and excecute the python code as part of the experiment:
  6. Add an "Execute Action" node from the "Action" tab, and insert it immediately after Start.
  7. Set the node "Execute method" attribute to @CUSTOM_CLASS_INSTANCE.myMethod@
  8. From the Execute Action node, insert a Conditional node, and put in the evaluation attributes @EXECUTE.result@, equals, 1. 
  9. From a Conditional node, have the valid condition leading to the rest of your experiment, whereas the invalid condition should lead to a display saying that Damm validation failed.

Python code:

import sreb

class CustomClassTemplate(sreb.EBObject):
    def __init__(self):
        sreb.EBObject.__init__(self)
        #NB!
        #Property1 holds the session name if you do this: 
        #Put =@parent.sessionName@ into property1 of the custom class instance attributes.
        self.property1=""    
        
    #Property: property1
    #A read and write string type property
    def setProperty1(self,c):
        self.property1=c
    
    def getProperty1(self):
        return self.property1
        
    #Callable method using Execute action.  
    #Note the default arguments and the doc string to let eb know what is the expected return type.
    def myMethod(self):
        """RETURN:int"""
        #The first line of the doc of method is used to get the return type of the method.

        # Damm code from https://en.wikibooks.org/wiki/Algorithm_Implementation/Checksums/Damm_Algorithm#Python
        # For reference see https://en.wikipedia.org/wiki/Damm_algorithm
        
        matrix = (
            (0, 3, 1, 7, 5, 9, 8, 6, 4, 2),
            (7, 0, 9, 2, 1, 5, 4, 8, 6, 3),
            (4, 2, 0, 6, 8, 7, 1, 3, 5, 9),
            (1, 7, 5, 0, 9, 8, 3, 4, 2, 6),
            (6, 1, 2, 3, 0, 4, 5, 9, 7, 8),
            (3, 6, 7, 4, 2, 0, 9, 5, 8, 1),
            (5, 8, 6, 9, 7, 2, 0, 1, 3, 4),
            (8, 9, 4, 5, 3, 6, 2, 0, 1, 7),
            (9, 4, 3, 8, 6, 1, 7, 2, 0, 5),
            (2, 5, 8, 1, 4, 3, 6, 7, 9, 0)
        )
        
        def encode(number):
            number = str(number)
            interim = 0   
            for digit in number:
                interim = matrix[interim][int(digit)]
            return interim
            
        def check(number):
            return encode(number) == 0
        
        
        def validate_participant_ID(id):
            # quick sanity checking (from wikibooks)
            assert encode(572) == 4 # from wikipedia
            assert check(5724)
            assert encode('43881234567') == 9 # hand-computed
            
            #Actual validation
            id = id.replace('\n','').replace('NO','')
            result = check(id)
            return result


        id = self.getProperty1()  #Property1 holds the session name
        result = validate_participant_ID(id)
        if result == True:
            return 1
        else:
            return 0
 

 

Code for pre-generating valid Damm values in Python

### Generate participant IDs for experiment

# Damm code from https://en.wikibooks.org/wiki/Algorithm_Implementation/Checksums/Damm_Algorithm#Python
# For reference see https://en.wikipedia.org/wiki/Damm_algorithm

matrix = (
    (0, 3, 1, 7, 5, 9, 8, 6, 4, 2),
    (7, 0, 9, 2, 1, 5, 4, 8, 6, 3),
    (4, 2, 0, 6, 8, 7, 1, 3, 5, 9),
    (1, 7, 5, 0, 9, 8, 3, 4, 2, 6),
    (6, 1, 2, 3, 0, 4, 5, 9, 7, 8),
    (3, 6, 7, 4, 2, 0, 9, 5, 8, 1),
    (5, 8, 6, 9, 7, 2, 0, 1, 3, 4),
    (8, 9, 4, 5, 3, 6, 2, 0, 1, 7),
    (9, 4, 3, 8, 6, 1, 7, 2, 0, 5),
    (2, 5, 8, 1, 4, 3, 6, 7, 9, 0)
)

def encode(number):
    number = str(number)
    interim = 0   
    for digit in number:
        interim = matrix[interim][int(digit)]
    return interim
    
def check(number):
    return encode(number) == 0


def generate_ids():
    # quick sanity checking (from wikibooks)
    assert encode(572) == 4 # from wikipedia
    assert check(5724)
    assert encode('43881234567') == 9 # hand-computed

    # this is the actual code generating the participant IDs
    Nsub = 120 # how many we want
    offs = 101 # added to each number to ensure (a) starting with 1 and (b) nonzero first digit
    sbcf = open("subjectIDcodes.txt","w")
    for sbj in range(Nsub):
        nm = sbj+offs
        nme = encode(nm)
        sbc = "NO%d%d"%(nm,nme)
        #print(sbc) # for checking
        sbcf.write(sbc+'\n')
    sbcf.close()

    
if __name__ == '__main__':
    generate_ids()

 

 

 

Av Anders Lunde
Publisert 24. feb. 2021 18:09 - Sist endret 24. feb. 2021 18:15