Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Coding help

59 views
Skip to first unread message

Benedek Kurdi

unread,
May 1, 2024, 11:58:53 AM5/1/24
to Minno.js

Dear Yoav,
The problem that I need help with is the following (thanks so much in advance!).
In the study we would like to ask participants to enter all ZIP codes that they have lived in since they were born. It would be nice if the data could be structured something like this:
Start year End year ZIP
1977 1986 06511
1986 2014 10001
2014 2022 48007
2022 Now 47906
  • Ideally, a new row would appear in the table once the previous row has already been filled out and the end year is not "now" (or 2024, or whatever we choose it to be).
  • It would be nice to implement some kind of validation to make sure that the years are contiguous (no years skipped or repeated).
  • It would also be nice to implement validation to make sure that the ZIP codes that have been entered are all valid.
  • It might be nice if the start year could be auto-populated based on the birth year that the participant has entered in the demographics form, but this would really just be icing on the cake and is not at all necessary if it's complicated to implement.
Please let me know if this makes sense; very happy to provide more info. And, again, thanks so much for your help.
—Benedek

Yoav Bar-Anan

unread,
May 21, 2024, 5:21:43 PM5/21/24
to Benedek Kurdi, Minno.js

Hi Benedek,

Our questionnaire component does not support such a form. Therefore, it is implemented using the custom task.

The custom task’s code is general enough to load any JavaScript and html files that anyone will develop for a task that is not supported by Minno:

// This is a custom task. We will use it to run out zips form.
define(function(){

    //First, this code will load the functions from our functions JavaScript file. They are needed for our html file.

    var script_ext = document.createElement('script');

    script_ext.src ="https://app-prod-03.implicit.harvard.edu/implicit/user/yba/zipstable//myfuncs.js";

    document.head.appendChild(script_ext);

    // the script object being returned
    return {

        // the activator function uses three dependencies
        play: function activator(done, script, $element, global){
            global.done = done; //done is the callback function to let Minno know this task is finished. We will call it from our functions file, so we need to save it into the global object.
            // Fetch the content of the html file
            fetch('https://app-prod-03.implicit.harvard.edu/implicit/user/yba/zipstable//ziptable.html')
            .then(response => response.text())
            .then(data => {
                $element.html(data);
            })  
            .catch(error => {
                console.error('Error fetching content from the second file', error);
            });


            // will be called at the end of the task to clean things up
            // (whether the end is forced or triggered by 'done')
            return function clear(){
                $element.empty();           
            };
        }
    };
});
  • Notice that the URLs are hardcoded here, which means you will need to change them to the files in your study.

Then, I created an html file that shows the table and the buttons, and a file with all the JavaScript functions needed for supporting most of the features you requested.

You can see all the files here, and launch the study here.

Currently, we set the first possible year in the manager file.It is hard-coded to 1901. If you want to use the year of birth entered upon registration to Project Implicit’s research pool, Andy showed me how to do that a few weeks ago. It requires changing the manager file to a jsp, and running some Java (not JavaScript) code to get the birth year. Here is my jsp manager file (well, because it is jsp, it will probably not show, so I duplicated it to a txt file, but remember that the file extension is supposed to be a jsp).

This is the relevant Java code:

StudySession studySession = (StudySession) session.getAttribute("studysession");
String age="35";
String userIsNull="yes";
RegisteredUser user=studySession.getUser();
if(user!=null){
    Demographics demo=user.getDemographics();
    age=demo.getValue("age");
    userIsNull="no";
}

Then, we set the age into a property of the global object with line

    global.pAge = <%=age%>;

You can use the age to compute the birth-year:

    global.pAge = 2024-<%=age%>;

Actually, I think that Andy told me that you can read the birth year directly:

StudySession studySession = (StudySession) session.getAttribute("studysession");
String   birthyear ="1900";
String userIsNull="yes";
RegisteredUser user=studySession.getUser();
if(user!=null){
    Demographics demo=user.getDemographics();
    birthyear=demo.getValue("birthyear");
    userIsNull="no";
}

You can also ask their birth year again in a questionnaire, and then enter their answer to global.minYear before running the zips tasks.

I hope that helps. It’s a lot, so let me know if you have more questions.

Yoav


--
You received this message because you are subscribed to the Google Groups "Minno.js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to minnojs+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/minnojs/CAPGF78GFKHhnPiwfpa_p5QfwLBwTP_pOQ7cP6%3DGVTc8a4ACh7g%40mail.gmail.com.

Benedek Kurdi

unread,
May 22, 2024, 4:07:11 PM5/22/24
to Yoav Bar-Anan, Minno.js

Hi Yoav,
Thank you so much, this is amazing! I was able to add these tasks to my study and everything is working great (the data are also getting saved properly).
I have just a few more questions:
(1) Birth year
I already have a pre-screening questionnaire anyway. I now added a birth year question to it, which could theoretically be used to propagate the first row of the table. I'm assuming that for this, the input to that question will have to be saved as a global variable. Do you have any code showing how to do that in the context of an explicit task?
(2) Instructions for filling out the table
I would like to include some instructions for filling out the table. In which file should I do this, and what formatting should I use?
(3) Hard-coded links
I was wondering what would happen to hard-coded links once the study is deployed. E.g., https://app-prod-03.implicit.harvard.edu/implicit/user/bkurdi/lifezip1//ziptable.html refers to the test server. Normally I just omit the first half of the URL until /implicit so that things don't get messed up once the study gets deployed. Would that be possible here too?
(4) Submit button
Would it be possible to make sure that the Submit button appears only once the participant has filled out the table completely (i.e., once the table has a last row with the second date being now)?
Thank you so much again, I really appreciate all your help.
—Benedek

Yoav Bar-Anan

unread,
May 24, 2024, 5:59:05 PM5/24/24
to Benedek Kurdi, Minno.js

Hi Benedek,

(1) The response to any question can be found through the global. For example, if you add the question yearofbirth to the questionnaire task named birthyear, you can find the participant's response at global.birthyear.questions.yearofbirth.response.
However, the zip tables implementation is unordinary, so in order to get the global object, we used its name piGlobal (that’s its name in JavaScript’s “global execution context” A.K.A “the global scope” [if you open Developer Tools and go to the console, you can always type piGlobal to see the whole object and what properties it has]).

So, here are the changes I made in myfuncs.js:

const response = piGlobal.birthyear?.questions?.yearofbirth?.response; //Read the birthyear, if it exists.
const parsedResponse = parseInt(response, 10); //convert to integer
const min = Number.isInteger(parsedResponse) ? parsedResponse : piGlobal.minYear; //If birthyear is a number, use it. If not, use the minYear.

//This function makes sure that the user did not enter at the top row a starting year that makes no sense.
function validateYearRange(input) {

    const max = parseInt(input.max);
    const value = parseInt(input.value);

    if (value < min) {
        input.min = min;
        input.value = min;
    }
    else if (value > max)
    {
        input.value = input.max;
    }
}

I did not populate the birthyear to the first “start year” by default, to allow participants to use a different year, in case they don’t know their first zip (I assume many don’t), and, just, generally, to leave some flexibility. If you’re sure you want it hardcoded and unchangable, I can suggest the relevant code.

(2) I added to the html file, style and html for the instructions.
The style:

    .panel-text {
        font-size: 16px;
        margin-top: 10px;
        margin-left: 10px;
        color: #555; /* Adjust the color to match your design */
    }

The html:

<div class="panel-text">
        The instructions for completing this form.
</div>

(3) I am not sure. Try the /implicit. If it works on dev, it probably will work on production. If you have to use the complete URL, then change the URLs before deploy to the same URL but without app-prod-03. . That is, https://implicit.harvard.edu/implicit/user/bkurdi/lifezip1/ziptable.html

(4) I can add code for that. But, before that, I just wanted to double-check that you’re sure. Currently, if participants try to submit without completing all the details and ending with “Now”, the validation function will show an error message. If you disable the Submit, they will have to find out why the Submit button is disabled without any error message to help them. Are you sure that’s your prefered behavior?

Yoav


Benedek Kurdi

unread,
May 26, 2024, 12:26:28 PM5/26/24
to Yoav Bar-Anan, Minno.js

Thank you so much for all of this, Yoav! The study is now working perfectly. I will test things out with a few hundred participants and then let you know if anything is not working as intended.
—Benedek
Reply all
Reply to author
Forward
0 new messages