Trials (1)

Source: demos/trial

Often psychological experiments are organized into ‘trials’. A trial is a single unit of data collection, which might typically involve recording the participant’s response to a single stimulus. PsyNet contains some sophisticated abstractions for working with trials in progressively more complex fashions. These are documented in detail elsewhere, but you can get a feel for an approach by looking at the demos.

This simple demo implements an experiment where participants have to give ratings for different animals. The demo is build around a custom class called RateTrial, which defines the logic for a given trial. The key element of this class is the show_trial method, which defines the page (or pages) shown to the participant. In the simplest case, this method just returns a single page, which will most commonly be a Modular Page.

Below this we define the word_ratings Module. Modules are a useful way for organizing the logic of PsyNet experiments into discrete components. This Module contains a For Loop, which here is used to sample three random words to present to the participant. To present a word in the form of a Rate Trial, we call RateTrial.cue.

Note

The test_check_bot method in the Experiment class is used to define a custom function for checking whether a bot has the expected state. It’s run by the automated PsyNet tests once a given bot has finished the experiment. For example, the code assert len(trials) == 3 will throw an error if the bot hasn’t completed exactly 3 trials.

import random

from markupsafe import Markup

import psynet.experiment
from psynet.bot import Bot
from psynet.consent import NoConsent
from psynet.modular_page import ModularPage, PushButtonControl
from psynet.page import SuccessfulEndPage
from psynet.timeline import Module, Timeline, for_loop
from psynet.trial.main import Trial

WORDS = [
    "cat",
    "dog",
    "fish",
    "monkey",
    "giraffe",
    "octopus",
]


class RateTrial(Trial):
    time_estimate = 3

    def show_trial(self, experiment, participant):
        word = self.definition["word"]

        return ModularPage(
            "rate_trial",
            Markup(f"How happy is the following word: <strong>{word}</strong>"),
            PushButtonControl(
                ["Not at all", "A little", "Very much"],
            ),
        )


word_ratings = Module(
    "word_ratings",
    for_loop(
        label="Randomly sample three words from the word list",
        iterate_over=lambda: random.sample(WORDS, 3),
        logic=lambda word: RateTrial.cue(
            {
                "word": word,
            }
        ),
        time_estimate_per_iteration=3,
    ),
)


class Exp(psynet.experiment.Experiment):
    label = "Simple trial demo"
    initial_recruitment_size = 1

    timeline = Timeline(
        NoConsent(),
        word_ratings,
        SuccessfulEndPage(),
    )

    test_n_bots = 3

    def test_check_bot(self, bot: Bot, **kwargs):
        assert not bot.failed
        trials = bot.all_trials
        assert len(trials) == 3
        assert len(set([t.definition["word"] for t in trials])) == 3
        assert all([t.definition["word"] in WORDS for t in trials])
        assert all([t.complete for t in trials])
        assert all([t.finalized for t in trials])