Source code for psynet.trial.static

from typing import List, Optional, Union

from psynet.trial.chain import ChainNetwork, ChainNode, ChainTrial, ChainTrialMaker

from ..utils import NoArgumentProvided, deep_copy, get_logger
from .main import Trial

logger = get_logger()


[docs] class StaticTrial(ChainTrial): """ A Trial class for static experiments. The user must override the ``time_estimate`` class attribute, providing the estimated duration of the trial in seconds. This is used for predicting the participant's reward and for constructing the progress bar. Attributes ---------- time_estimate : numeric The estimated duration of the trial (including any feedback), in seconds. This should generally correspond to the (sum of the) ``time_estimate`` parameters in the page(s) generated by ``show_trial``, plus the ``time_estimate`` parameter in the page generated by ``show_feedback`` (if defined). This is used for predicting the participant's reward and for constructing the progress bar. participant_id : int The ID of the associated participant. The user should not typically change this directly. Stored in ``property1`` in the database. complete : bool Whether the trial has been completed (i.e. received a response from the participant). The user should not typically change this directly. Stored in ``property2`` in the database. answer : Object The response returned by the participant. This is serialised to JSON, so it shouldn't be too big. The user should not typically change this directly. Stored in ``details`` in the database. earliest_async_process_start_time : Optional[datetime] Time at which the earliest pending async process was called. definition A dictionary of parameters defining the trial, inherited from the respective :class:`~psynet.trial.static.Stimulus` object. participant_group The associated participant group. block The block in which the trial is situated. """ __extra_vars__ = Trial.__extra_vars__.copy() def generate_asset_key(self, asset): return f"{self.trial_maker_id}/block_{self.block}__node_{self.node_id}__trial_{self.id}__{asset.local_key}{asset.extension}"
[docs] def show_trial(self, experiment, participant): raise NotImplementedError
[docs] def make_definition(self, experiment, participant): for k, v in self.node.assets.items(): self.assets[k] = v return deep_copy(self.node.definition)
[docs] class StaticTrialMaker(ChainTrialMaker): """ Administers a sequence of trials in a static experiment. The class is intended for use with the :class:`~psynet.trial.static.StaticTrial` helper class. which should be customised to show the relevant node for the experimental paradigm. The user may also override the following methods, if desired: * :meth:`~psynet.trial.static.StaticTrialMaker.choose_block_order`; chooses the order of blocks in the experiment. By default the blocks are ordered randomly. * :meth:`~psynet.trial.static.StaticTrialMaker.choose_participant_group`; Only relevant if the trial maker uses nodes with non-default participant groups. In this case the experimenter is expected to supply a function that takes participant as an argument and returns the chosen participant group for that trial maker. * :meth:`~psynet.trial.main.TrialMaker.on_complete`, run once the sequence of trials is complete. * :meth:`~psynet.trial.main.TrialMaker.performance_check`; checks the performance of the participant with a view to rejecting poor-performing participants. * :meth:`~psynet.trial.main.TrialMaker.compute_performance_reward`; computes the final performance reward to assign to the participant. Further customisable options are available in the constructor's parameter list, documented below. Parameters ---------- trial_class The class object for trials administered by this maker (should subclass :class:`~psynet.trial.static.StaticTrial`). nodes The nodes to be administered to the participants. This can be provided as a list of :class:`~psynet.trial.static.StaticNode` objects, or as a function (taking no arguments) that can be called to generate such a list. The latter is useful for generating nodes based on local files (e.g. large media assets) that are not available on the deployed server. expected_trials_per_participant Expected number of trials that each participant will complete. This is used for timeline/progress estimation purposes. max_trials_per_participant Maximum number of trials that each participant may complete; once this number is reached, the participant will move on to the next stage in the timeline. recruit_mode Selects a recruitment criterion for determining whether to recruit another participant. The built-in criteria are ``"n_participants"`` and ``"n_trials"``. target_n_participants Target number of participants to recruit for the experiment. All participants must successfully finish the experiment to count towards this quota. This target is only relevant if ``recruit_mode="n_participants"``. target_trials_per_node Target number of trials to recruit for each node in the experiment. This target is only relevant if ``recruit_mode="n_trials"``. max_trials_per_block Determines the maximum number of trials that a participant will be allowed to experience in each block, including failed trials. Note that this number does not include repeat trials. allow_repeated_nodes Determines whether the participant can be administered the same node more than once. max_unique_nodes_per_block Determines the maximum number of unique nodes that a participant will be allowed to experience in each block. Once this quota is reached, the participant will be forced to repeat previously experienced nodes. balance_across_nodes If ``True`` (default), active balancing across participants is enabled, meaning that node selection favours nodes that have been presented fewest times to any participant in the experiment, excluding failed trials. check_performance_at_end If ``True``, the participant's performance is evaluated at the end of the series of trials. Defaults to ``False``. See :meth:`~psynet.trial.main.TrialMaker.performance_check` for implementing performance checks. check_performance_every_trial If ``True``, the participant's performance is evaluated after each trial. Defaults to ``False``. See :meth:`~psynet.trial.main.TrialMaker.performance_check` for implementing performance checks. fail_trials_on_premature_exit If ``True``, a participant's trials are marked as failed if they leave the experiment prematurely. Defaults to ``True``. fail_trials_on_participant_performance_check If ``True``, a participant's trials are marked as failed if the participant fails a performance check. Defaults to ``True``. n_repeat_trials Number of repeat trials to present to the participant. These trials are typically used to estimate the reliability of the participant's responses. Repeat trials are presented at the end of the trial maker, after all blocks have been completed. Defaults to 0. choose_participant_group Only relevant if the trial maker uses nodes with non-default participant groups. In this case the experimenter is expected to supply a function that takes participant as an argument and returns the chosen participant group for that trial maker. sync_group_type Optional SyncGroup type to use for synchronizing participant allocation to nodes. When this is set, then the ordinary node allocation logic will only apply to the 'leader' of each SyncGroup. The other members of this SyncGroup will follow that leader around, so that in every given trial the SyncGroup works on the same node together. Attributes ---------- check_timeout_interval_sec : float How often to check for trials that have timed out, in seconds (default = 30). Users are invited to override this. response_timeout_sec : float How long until a trial's response times out, in seconds (default = 60) (i.e. how long PsyNet will wait for the participant's response to a trial). This is a lower bound on the actual timeout time, which depends on when the timeout daemon next runs, which in turn depends on :attr:`~psynet.trial.main.TrialMaker.check_timeout_interval_sec`. Users are invited to override this. async_timeout_sec : float How long until an async process times out, in seconds (default = 300). This is a lower bound on the actual timeout time, which depends on when the timeout daemon next runs, which in turn depends on :attr:`~psynet.trial.main.TrialMaker.check_timeout_interval_sec`. Users are invited to override this. network_query : sqlalchemy.orm.Query An SQLAlchemy query for retrieving all networks owned by the current trial maker. Can be used for operations such as the following: ``self.network_query.count()``. n_networks : int Returns the number of networks owned by the trial maker. networks : list Returns the networks owned by the trial maker. performance_threshold : float Score threshold used by the default performance check method, defaults to 0.0. By default, corresponds to the minimum proportion of non-failed trials that the participant must achieve to pass the performance check. end_performance_check_waits : bool If ``True`` (default), then the final performance check waits until all trials no longer have any pending asynchronous processes. """ def __init__( self, *, id_: str, trial_class, nodes: Optional[Union[callable, List["StaticNode"]]], expected_trials_per_participant: int, max_trials_per_participant: Optional[int] = NoArgumentProvided, recruit_mode: Optional[str] = None, target_n_participants: Optional[int] = None, target_trials_per_node: Optional[int] = None, max_trials_per_block: Optional[int] = None, allow_repeated_nodes: bool = False, balance_across_nodes: bool = True, check_performance_at_end: bool = False, check_performance_every_trial: bool = False, fail_trials_on_premature_exit: bool = True, fail_trials_on_participant_performance_check: bool = True, n_repeat_trials: int = 0, assets=None, choose_participant_group: Optional[callable] = None, sync_group_type: Optional[str] = None, ): # balance_across_chains = ( # active_balancing_across_participants or active_balancing_within_participants # ) # balance_strategy = set() # if active_balancing_within_participants: # balance_strategy.add("within") # if active_balancing_across_participants: # balance_strategy.add("across") if callable(nodes): if expected_trials_per_participant is None: raise ValueError( "If nodes is a function, expected_trials_per_participant must be explicitly provided." ) chains_per_experiment = None else: assert isinstance(nodes, list) if ( expected_trials_per_participant > len(nodes) and not allow_repeated_nodes ): raise ValueError( f"expected_trials_per_participant ({expected_trials_per_participant}) " f"may not exceed len(nodes) ({len(nodes)}) " "unless allow_repeated_nodes = True." ) chains_per_experiment = len(nodes) if allow_repeated_nodes: assert ( max_trials_per_participant is not None or max_trials_per_block is not None ) super().__init__( id_=id_, start_nodes=nodes, trial_class=trial_class, network_class=StaticNetwork, node_class=StaticNode, recruit_mode=recruit_mode, target_n_participants=target_n_participants, expected_trials_per_participant=expected_trials_per_participant, max_trials_per_participant=max_trials_per_participant, max_trials_per_block=max_trials_per_block, chain_type="across", chains_per_participant=None, chains_per_experiment=chains_per_experiment, max_nodes_per_chain=1, trials_per_node=target_trials_per_node if target_trials_per_node else 1e6, balance_across_chains=balance_across_nodes, # balance_strategy=balance_strategy, allow_revisiting_networks_in_across_chains=allow_repeated_nodes, check_performance_at_end=check_performance_at_end, check_performance_every_trial=check_performance_every_trial, fail_trials_on_premature_exit=fail_trials_on_premature_exit, fail_trials_on_participant_performance_check=fail_trials_on_participant_performance_check, n_repeat_trials=n_repeat_trials, assets=assets, choose_participant_group=choose_participant_group, sync_group_type=sync_group_type, )
[docs] class StaticNetwork(ChainNetwork): pass
[docs] class StaticNode(ChainNode):
[docs] def summarize_trials(self, trials: list, experiment, participant): return None
[docs] def create_definition_from_seed(self, seed, experiment, participant): return None