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). A compatible example Experiment Builder source file (.ebz) is provided here.

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 project file is provided here (example.ebz), 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). To view the experiment builder file, unpack the project and open the graph.ebd file.

  1. Go to edit > preferences > experiment > "enable custom class"
  2. Go to edit > library manager > Select the "custom class" tab and click the "New" button.
  3. In the "File Name" dialog box, enter the intended custom class file name "damm_validation". (To load an existing python file .py as a new custom class, click the "Add" button)
  4. Add a new Custom Class instance to the experiment (found in the "Other" tab).
  5. Double-click the custom class instance, and replace all code with the provided python code (found below).
  6. Select Custom Class and enter "damm_validation.CustomClassTemplate" in the custom class instance properties.
  7. Put =@parent.sessionName@ into property1 of the custom class instance attributes.
  8. 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. To incorporate and execute the python code as part of the experiment:
  9. Add an "Execution Action" node from the "Action" tab, and insert it immediately after Start.
  10. Set the node "Execute method" attribute to @CUSTOM_CLASS_INSTANCE.myMethod@
  11. From the Execute Action node, insert a Conditional node from the "Trigger" tab, and put in the evaluation attributes @EXECUTE.result@, equals, 1.
  12. 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.

Note: It is possible to filter the session id prior to validation: 

For example, in the provided python code, the characters "NO" are removed from the session id prior to Damm validation. 

If you want to change the character string before the number of the session id, go to the provided Python code (see below), scroll down to the comment #Actual validation, and replace ‘NO’ with the new character(s). Then, click save to keep the revised python file, and go to the “update attribute action” node to change (if needed) the characters position range to be selected for the validation.

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 4. okt. 2021 10:54