mirror of
https://github.com/psychopy/psychojs.git
synced 2025-05-10 18:50:54 +00:00
data: enforce formatting rules
This commit is contained in:
parent
5468898716
commit
27d08ba42f
@ -7,12 +7,10 @@
|
|||||||
* @license Distributed under the terms of the MIT License
|
* @license Distributed under the terms of the MIT License
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import * as XLSX from "xlsx";
|
||||||
import * as XLSX from 'xlsx';
|
import { MonotonicClock } from "../util/Clock.js";
|
||||||
import {PsychObject} from '../util/PsychObject.js';
|
import { PsychObject } from "../util/PsychObject.js";
|
||||||
import {MonotonicClock} from '../util/Clock.js';
|
import * as util from "../util/Util.js";
|
||||||
import * as util from '../util/Util.js';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>An ExperimentHandler keeps track of multiple loops and handlers. It is particularly useful
|
* <p>An ExperimentHandler keeps track of multiple loops and handlers. It is particularly useful
|
||||||
@ -29,7 +27,6 @@ import * as util from '../util/Util.js';
|
|||||||
*/
|
*/
|
||||||
export class ExperimentHandler extends PsychObject
|
export class ExperimentHandler extends PsychObject
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for experimentEnded.
|
* Getter for experimentEnded.
|
||||||
*
|
*
|
||||||
@ -54,7 +51,6 @@ export class ExperimentHandler extends PsychObject
|
|||||||
this._experimentEnded = ended;
|
this._experimentEnded = ended;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Legacy experiment getters.
|
* Legacy experiment getters.
|
||||||
*/
|
*/
|
||||||
@ -68,16 +64,15 @@ export class ExperimentHandler extends PsychObject
|
|||||||
return this._trialsData;
|
return this._trialsData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
psychoJS,
|
psychoJS,
|
||||||
name,
|
name,
|
||||||
extraInfo
|
extraInfo,
|
||||||
} = {})
|
} = {})
|
||||||
{
|
{
|
||||||
super(psychoJS, name);
|
super(psychoJS, name);
|
||||||
|
|
||||||
this._addAttribute('extraInfo', extraInfo);
|
this._addAttribute("extraInfo", extraInfo);
|
||||||
|
|
||||||
// loop handlers:
|
// loop handlers:
|
||||||
this._loops = [];
|
this._loops = [];
|
||||||
@ -91,7 +86,6 @@ export class ExperimentHandler extends PsychObject
|
|||||||
this._experimentEnded = false;
|
this._experimentEnded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the current entry (i.e. trial data) is empty.
|
* Whether or not the current entry (i.e. trial data) is empty.
|
||||||
* <p>Note: this is mostly useful at the end of an experiment, in order to ensure that the last entry is saved.</p>
|
* <p>Note: this is mostly useful at the end of an experiment, in order to ensure that the last entry is saved.</p>
|
||||||
@ -106,7 +100,6 @@ export class ExperimentHandler extends PsychObject
|
|||||||
return (Object.keys(this._currentTrialData).length > 0);
|
return (Object.keys(this._currentTrialData).length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a loop.
|
* Add a loop.
|
||||||
*
|
*
|
||||||
@ -125,7 +118,6 @@ export class ExperimentHandler extends PsychObject
|
|||||||
loop.experimentHandler = this;
|
loop.experimentHandler = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the given loop from the list of unfinished loops, e.g. when it has completed.
|
* Remove the given loop from the list of unfinished loops, e.g. when it has completed.
|
||||||
*
|
*
|
||||||
@ -143,7 +135,6 @@ export class ExperimentHandler extends PsychObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the key/value pair.
|
* Add the key/value pair.
|
||||||
*
|
*
|
||||||
@ -172,7 +163,6 @@ export class ExperimentHandler extends PsychObject
|
|||||||
this._currentTrialData[key] = value;
|
this._currentTrialData[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inform this ExperimentHandler that the current trial has ended. Further calls to {@link addData}
|
* Inform this ExperimentHandler that the current trial has ended. Further calls to {@link addData}
|
||||||
* will be associated with the next trial.
|
* will be associated with the next trial.
|
||||||
@ -184,7 +174,7 @@ export class ExperimentHandler extends PsychObject
|
|||||||
*/
|
*/
|
||||||
nextEntry(snapshots)
|
nextEntry(snapshots)
|
||||||
{
|
{
|
||||||
if (typeof snapshots !== 'undefined')
|
if (typeof snapshots !== "undefined")
|
||||||
{
|
{
|
||||||
// turn single snapshot into a one-element array:
|
// turn single snapshot into a one-element array:
|
||||||
if (!Array.isArray(snapshots))
|
if (!Array.isArray(snapshots))
|
||||||
@ -203,7 +193,6 @@ export class ExperimentHandler extends PsychObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// this is to support legacy generated JavaScript code and does not properly handle
|
// this is to support legacy generated JavaScript code and does not properly handle
|
||||||
// loops within loops:
|
// loops within loops:
|
||||||
@ -236,7 +225,6 @@ export class ExperimentHandler extends PsychObject
|
|||||||
this._currentTrialData = {};
|
this._currentTrialData = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the results of the experiment.
|
* Save the results of the experiment.
|
||||||
*
|
*
|
||||||
@ -254,11 +242,11 @@ export class ExperimentHandler extends PsychObject
|
|||||||
* @param {Array.<Object>} [options.sync] - whether or not to communicate with the server in a synchronous manner
|
* @param {Array.<Object>} [options.sync] - whether or not to communicate with the server in a synchronous manner
|
||||||
*/
|
*/
|
||||||
async save({
|
async save({
|
||||||
attributes = [],
|
attributes = [],
|
||||||
sync = false
|
sync = false,
|
||||||
} = {})
|
} = {})
|
||||||
{
|
{
|
||||||
this._psychoJS.logger.info('[PsychoJS] Save experiment results.');
|
this._psychoJS.logger.info("[PsychoJS] Save experiment results.");
|
||||||
|
|
||||||
// (*) get attributes:
|
// (*) get attributes:
|
||||||
if (attributes.length === 0)
|
if (attributes.length === 0)
|
||||||
@ -286,16 +274,14 @@ export class ExperimentHandler extends PsychObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// (*) get various experiment info:
|
// (*) get various experiment info:
|
||||||
const info = this.extraInfo;
|
const info = this.extraInfo;
|
||||||
const __experimentName = (typeof info.expName !== 'undefined') ? info.expName : this.psychoJS.config.experiment.name;
|
const __experimentName = (typeof info.expName !== "undefined") ? info.expName : this.psychoJS.config.experiment.name;
|
||||||
const __participant = ((typeof info.participant === 'string' && info.participant.length > 0) ? info.participant : 'PARTICIPANT');
|
const __participant = ((typeof info.participant === "string" && info.participant.length > 0) ? info.participant : "PARTICIPANT");
|
||||||
const __session = ((typeof info.session === 'string' && info.session.length > 0) ? info.session : 'SESSION');
|
const __session = ((typeof info.session === "string" && info.session.length > 0) ? info.session : "SESSION");
|
||||||
const __datetime = ((typeof info.date !== 'undefined') ? info.date : MonotonicClock.getDateStr());
|
const __datetime = ((typeof info.date !== "undefined") ? info.date : MonotonicClock.getDateStr());
|
||||||
const gitlabConfig = this._psychoJS.config.gitlab;
|
const gitlabConfig = this._psychoJS.config.gitlab;
|
||||||
const __projectId = (typeof gitlabConfig !== 'undefined' && typeof gitlabConfig.projectId !== 'undefined') ? gitlabConfig.projectId : undefined;
|
const __projectId = (typeof gitlabConfig !== "undefined" && typeof gitlabConfig.projectId !== "undefined") ? gitlabConfig.projectId : undefined;
|
||||||
|
|
||||||
|
|
||||||
// (*) save to a .csv file:
|
// (*) save to a .csv file:
|
||||||
if (this._psychoJS.config.experiment.saveFormat === ExperimentHandler.SaveFormat.CSV)
|
if (this._psychoJS.config.experiment.saveFormat === ExperimentHandler.SaveFormat.CSV)
|
||||||
@ -304,23 +290,23 @@ export class ExperimentHandler extends PsychObject
|
|||||||
// newlines, etc.
|
// newlines, etc.
|
||||||
const worksheet = XLSX.utils.json_to_sheet(this._trialsData);
|
const worksheet = XLSX.utils.json_to_sheet(this._trialsData);
|
||||||
// prepend BOM
|
// prepend BOM
|
||||||
const csv = '\ufeff' + XLSX.utils.sheet_to_csv(worksheet);
|
const csv = "\ufeff" + XLSX.utils.sheet_to_csv(worksheet);
|
||||||
|
|
||||||
// upload data to the pavlovia server or offer them for download:
|
// upload data to the pavlovia server or offer them for download:
|
||||||
const key = __participant + '_' + __experimentName + '_' + __datetime + '.csv';
|
const key = __participant + "_" + __experimentName + "_" + __datetime + ".csv";
|
||||||
if (this._psychoJS.getEnvironment() === ExperimentHandler.Environment.SERVER &&
|
if (
|
||||||
this._psychoJS.config.experiment.status === 'RUNNING' &&
|
this._psychoJS.getEnvironment() === ExperimentHandler.Environment.SERVER
|
||||||
!this._psychoJS._serverMsg.has('__pilotToken'))
|
&& this._psychoJS.config.experiment.status === "RUNNING"
|
||||||
|
&& !this._psychoJS._serverMsg.has("__pilotToken")
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return /*await*/ this._psychoJS.serverManager.uploadData(key, csv, sync);
|
return /*await*/ this._psychoJS.serverManager.uploadData(key, csv, sync);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
util.offerDataForDownload(key, csv, 'text/csv');
|
util.offerDataForDownload(key, csv, "text/csv");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// (*) save in the database on the remote server:
|
// (*) save in the database on the remote server:
|
||||||
else if (this._psychoJS.config.experiment.saveFormat === ExperimentHandler.SaveFormat.DATABASE)
|
else if (this._psychoJS.config.experiment.saveFormat === ExperimentHandler.SaveFormat.DATABASE)
|
||||||
{
|
{
|
||||||
@ -328,7 +314,7 @@ export class ExperimentHandler extends PsychObject
|
|||||||
|
|
||||||
for (let r = 0; r < this._trialsData.length; r++)
|
for (let r = 0; r < this._trialsData.length; r++)
|
||||||
{
|
{
|
||||||
let doc = {__projectId, __experimentName, __participant, __session, __datetime};
|
let doc = { __projectId, __experimentName, __participant, __session, __datetime };
|
||||||
for (let h = 0; h < attributes.length; h++)
|
for (let h = 0; h < attributes.length; h++)
|
||||||
{
|
{
|
||||||
doc[attributes[h]] = this._trialsData[r][attributes[h]];
|
doc[attributes[h]] = this._trialsData[r][attributes[h]];
|
||||||
@ -338,22 +324,22 @@ export class ExperimentHandler extends PsychObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
// upload data to the pavlovia server or offer them for download:
|
// upload data to the pavlovia server or offer them for download:
|
||||||
if (this._psychoJS.getEnvironment() === ExperimentHandler.Environment.SERVER &&
|
if (
|
||||||
this._psychoJS.config.experiment.status === 'RUNNING' &&
|
this._psychoJS.getEnvironment() === ExperimentHandler.Environment.SERVER
|
||||||
!this._psychoJS._serverMsg.has('__pilotToken'))
|
&& this._psychoJS.config.experiment.status === "RUNNING"
|
||||||
|
&& !this._psychoJS._serverMsg.has("__pilotToken")
|
||||||
|
)
|
||||||
{
|
{
|
||||||
const key = 'results'; // name of the mongoDB collection
|
const key = "results"; // name of the mongoDB collection
|
||||||
return /*await*/ this._psychoJS.serverManager.uploadData(key, JSON.stringify(documents), sync);
|
return /*await*/ this._psychoJS.serverManager.uploadData(key, JSON.stringify(documents), sync);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
util.offerDataForDownload('results.json', JSON.stringify(documents), 'application/json');
|
util.offerDataForDownload("results.json", JSON.stringify(documents), "application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the attribute names and values for the current trial of a given loop.
|
* Get the attribute names and values for the current trial of a given loop.
|
||||||
* <p> Only info relating to the trial execution are returned.</p>
|
* <p> Only info relating to the trial execution are returned.</p>
|
||||||
@ -367,20 +353,20 @@ export class ExperimentHandler extends PsychObject
|
|||||||
static _getLoopAttributes(loop)
|
static _getLoopAttributes(loop)
|
||||||
{
|
{
|
||||||
// standard trial attributes:
|
// standard trial attributes:
|
||||||
const properties = ['thisRepN', 'thisTrialN', 'thisN', 'thisIndex', 'stepSizeCurrent', 'ran', 'order'];
|
const properties = ["thisRepN", "thisTrialN", "thisN", "thisIndex", "stepSizeCurrent", "ran", "order"];
|
||||||
let attributes = {};
|
let attributes = {};
|
||||||
const loopName = loop.name;
|
const loopName = loop.name;
|
||||||
for (const loopProperty in loop)
|
for (const loopProperty in loop)
|
||||||
{
|
{
|
||||||
if (properties.includes(loopProperty))
|
if (properties.includes(loopProperty))
|
||||||
{
|
{
|
||||||
const key = (loopProperty === 'stepSizeCurrent') ? loopName + '.stepSize' : loopName + '.' + loopProperty;
|
const key = (loopProperty === "stepSizeCurrent") ? loopName + ".stepSize" : loopName + "." + loopProperty;
|
||||||
attributes[key] = loop[loopProperty];
|
attributes[key] = loop[loopProperty];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// specific trial attributes:
|
// specific trial attributes:
|
||||||
if (typeof loop.getCurrentTrial === 'function')
|
if (typeof loop.getCurrentTrial === "function")
|
||||||
{
|
{
|
||||||
const currentTrial = loop.getCurrentTrial();
|
const currentTrial = loop.getCurrentTrial();
|
||||||
for (const trialProperty in currentTrial)
|
for (const trialProperty in currentTrial)
|
||||||
@ -404,7 +390,7 @@ export class ExperimentHandler extends PsychObject
|
|||||||
else:
|
else:
|
||||||
names.append(loopName+'.thisTrial')
|
names.append(loopName+'.thisTrial')
|
||||||
vals.append(trial)
|
vals.append(trial)
|
||||||
|
|
||||||
// single StairHandler
|
// single StairHandler
|
||||||
elif hasattr(loop, 'intensities'):
|
elif hasattr(loop, 'intensities'):
|
||||||
names.append(loopName+'.intensity')
|
names.append(loopName+'.intensity')
|
||||||
@ -415,10 +401,8 @@ export class ExperimentHandler extends PsychObject
|
|||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Experiment result format
|
* Experiment result format
|
||||||
*
|
*
|
||||||
@ -431,15 +415,14 @@ ExperimentHandler.SaveFormat = {
|
|||||||
/**
|
/**
|
||||||
* Results are saved to a .csv file
|
* Results are saved to a .csv file
|
||||||
*/
|
*/
|
||||||
CSV: Symbol.for('CSV'),
|
CSV: Symbol.for("CSV"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Results are saved to a database
|
* Results are saved to a database
|
||||||
*/
|
*/
|
||||||
DATABASE: Symbol.for('DATABASE')
|
DATABASE: Symbol.for("DATABASE"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Experiment environment.
|
* Experiment environment.
|
||||||
*
|
*
|
||||||
@ -448,6 +431,6 @@ ExperimentHandler.SaveFormat = {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
ExperimentHandler.Environment = {
|
ExperimentHandler.Environment = {
|
||||||
SERVER: Symbol.for('SERVER'),
|
SERVER: Symbol.for("SERVER"),
|
||||||
LOCAL: Symbol.for('LOCAL')
|
LOCAL: Symbol.for("LOCAL"),
|
||||||
};
|
};
|
||||||
|
@ -9,11 +9,10 @@
|
|||||||
* @license Distributed under the terms of the MIT License
|
* @license Distributed under the terms of the MIT License
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import seedrandom from "seedrandom";
|
||||||
import seedrandom from 'seedrandom';
|
import * as XLSX from "xlsx";
|
||||||
import * as XLSX from 'xlsx';
|
import { PsychObject } from "../util/PsychObject.js";
|
||||||
import {PsychObject} from '../util/PsychObject.js';
|
import * as util from "../util/Util.js";
|
||||||
import * as util from '../util/Util.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>A Trial Handler handles the importing and sequencing of conditions.</p>
|
* <p>A Trial Handler handles the importing and sequencing of conditions.</p>
|
||||||
@ -31,7 +30,6 @@ import * as util from '../util/Util.js';
|
|||||||
*/
|
*/
|
||||||
export class TrialHandler extends PsychObject
|
export class TrialHandler extends PsychObject
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for experimentHandler.
|
* Getter for experimentHandler.
|
||||||
*
|
*
|
||||||
@ -56,7 +54,6 @@ export class TrialHandler extends PsychObject
|
|||||||
this._experimentHandler = exp;
|
this._experimentHandler = exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
* @public
|
* @public
|
||||||
@ -64,27 +61,27 @@ export class TrialHandler extends PsychObject
|
|||||||
* @todo extraInfo is not taken into account, we use the expInfo of the ExperimentHandler instead
|
* @todo extraInfo is not taken into account, we use the expInfo of the ExperimentHandler instead
|
||||||
*/
|
*/
|
||||||
constructor({
|
constructor({
|
||||||
psychoJS,
|
psychoJS,
|
||||||
trialList = [undefined],
|
trialList = [undefined],
|
||||||
nReps,
|
nReps,
|
||||||
method = TrialHandler.Method.RANDOM,
|
method = TrialHandler.Method.RANDOM,
|
||||||
extraInfo = [],
|
extraInfo = [],
|
||||||
seed,
|
seed,
|
||||||
name,
|
name,
|
||||||
autoLog = true
|
autoLog = true,
|
||||||
} = {})
|
} = {})
|
||||||
{
|
{
|
||||||
super(psychoJS);
|
super(psychoJS);
|
||||||
|
|
||||||
this._addAttribute('trialList', trialList);
|
this._addAttribute("trialList", trialList);
|
||||||
this._addAttribute('nReps', nReps);
|
this._addAttribute("nReps", nReps);
|
||||||
this._addAttribute('method', method);
|
this._addAttribute("method", method);
|
||||||
this._addAttribute('extraInfo', extraInfo);
|
this._addAttribute("extraInfo", extraInfo);
|
||||||
this._addAttribute('name', name);
|
this._addAttribute("name", name);
|
||||||
this._addAttribute('autoLog', autoLog);
|
this._addAttribute("autoLog", autoLog);
|
||||||
this._addAttribute('seed', seed);
|
this._addAttribute("seed", seed);
|
||||||
this._prepareTrialList(trialList);
|
this._prepareTrialList(trialList);
|
||||||
|
|
||||||
// number of stimuli
|
// number of stimuli
|
||||||
this.nStim = this.trialList.length;
|
this.nStim = this.trialList.length;
|
||||||
|
|
||||||
@ -112,7 +109,6 @@ export class TrialHandler extends PsychObject
|
|||||||
// array of current snapshots:
|
// array of current snapshots:
|
||||||
this._snapshots = [];
|
this._snapshots = [];
|
||||||
|
|
||||||
|
|
||||||
// setup the trial sequence:
|
// setup the trial sequence:
|
||||||
this._prepareSequence();
|
this._prepareSequence();
|
||||||
|
|
||||||
@ -121,18 +117,17 @@ export class TrialHandler extends PsychObject
|
|||||||
this._finished = false;
|
this._finished = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helps go through each trial in the sequence one by one, mirrors PsychoPy.
|
* Helps go through each trial in the sequence one by one, mirrors PsychoPy.
|
||||||
*/
|
*/
|
||||||
next() {
|
next()
|
||||||
|
{
|
||||||
const trialIterator = this[Symbol.iterator]();
|
const trialIterator = this[Symbol.iterator]();
|
||||||
const { value } = trialIterator.next();
|
const { value } = trialIterator.next();
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterator over the trial sequence.
|
* Iterator over the trial sequence.
|
||||||
*
|
*
|
||||||
@ -168,7 +163,7 @@ export class TrialHandler extends PsychObject
|
|||||||
if (this.thisRepN >= this.nReps)
|
if (this.thisRepN >= this.nReps)
|
||||||
{
|
{
|
||||||
this.thisTrial = null;
|
this.thisTrial = null;
|
||||||
return {done: true};
|
return { done: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
this.thisIndex = this._trialSequence[this.thisRepN][this.thisTrialN];
|
this.thisIndex = this._trialSequence[this.thisRepN][this.thisTrialN];
|
||||||
@ -181,12 +176,11 @@ export class TrialHandler extends PsychObject
|
|||||||
vals = (self.thisRepN, self.thisTrialN, self.thisTrial)
|
vals = (self.thisRepN, self.thisTrialN, self.thisTrial)
|
||||||
logging.exp(msg % vals, obj=self.thisTrial)*/
|
logging.exp(msg % vals, obj=self.thisTrial)*/
|
||||||
|
|
||||||
return {value: this.thisTrial, done: false};
|
return { value: this.thisTrial, done: false };
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the callback for each trial in the sequence.
|
* Execute the callback for each trial in the sequence.
|
||||||
*
|
*
|
||||||
@ -208,7 +202,6 @@ export class TrialHandler extends PsychObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Snapshot
|
* @typedef {Object} Snapshot
|
||||||
* @property {TrialHandler} handler - the trialHandler
|
* @property {TrialHandler} handler - the trialHandler
|
||||||
@ -253,12 +246,12 @@ export class TrialHandler extends PsychObject
|
|||||||
getCurrentTrial: () => this.getTrial(currentIndex),
|
getCurrentTrial: () => this.getTrial(currentIndex),
|
||||||
getTrial: (index = 0) => this.getTrial(index),
|
getTrial: (index = 0) => this.getTrial(index),
|
||||||
|
|
||||||
addData: (key, value) => this.addData(key, value)
|
addData: (key, value) => this.addData(key, value),
|
||||||
};
|
};
|
||||||
|
|
||||||
// add to the snapshots the current trial's attributes:
|
// add to the snapshots the current trial's attributes:
|
||||||
const currentTrial = this.getCurrentTrial();
|
const currentTrial = this.getCurrentTrial();
|
||||||
const excludedAttributes = ['handler', 'name', 'nStim', 'nRemaining', 'thisRepN', 'thisTrialN', 'thisN', 'thisIndex', 'ran', 'finished'];
|
const excludedAttributes = ["handler", "name", "nStim", "nRemaining", "thisRepN", "thisTrialN", "thisN", "thisIndex", "ran", "finished"];
|
||||||
const trialAttributes = [];
|
const trialAttributes = [];
|
||||||
for (const attribute in currentTrial)
|
for (const attribute in currentTrial)
|
||||||
{
|
{
|
||||||
@ -280,7 +273,6 @@ export class TrialHandler extends PsychObject
|
|||||||
return snapshot;
|
return snapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setter for the seed attribute.
|
* Setter for the seed attribute.
|
||||||
*
|
*
|
||||||
@ -289,9 +281,9 @@ export class TrialHandler extends PsychObject
|
|||||||
*/
|
*/
|
||||||
setSeed(seed, log)
|
setSeed(seed, log)
|
||||||
{
|
{
|
||||||
this._setAttribute('seed', seed, log);
|
this._setAttribute("seed", seed, log);
|
||||||
|
|
||||||
if (typeof seed !== 'undefined')
|
if (typeof seed !== "undefined")
|
||||||
{
|
{
|
||||||
this._randomNumberGenerator = seedrandom(seed);
|
this._randomNumberGenerator = seedrandom(seed);
|
||||||
}
|
}
|
||||||
@ -301,7 +293,6 @@ export class TrialHandler extends PsychObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the internal state of this trial handler from the given snapshot.
|
* Set the internal state of this trial handler from the given snapshot.
|
||||||
*
|
*
|
||||||
@ -312,12 +303,11 @@ export class TrialHandler extends PsychObject
|
|||||||
static fromSnapshot(snapshot)
|
static fromSnapshot(snapshot)
|
||||||
{
|
{
|
||||||
// if snapshot is undefined, do nothing:
|
// if snapshot is undefined, do nothing:
|
||||||
if (typeof snapshot === 'undefined')
|
if (typeof snapshot === "undefined")
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
snapshot.handler.nStim = snapshot.nStim;
|
snapshot.handler.nStim = snapshot.nStim;
|
||||||
snapshot.handler.nTotal = snapshot.nTotal;
|
snapshot.handler.nTotal = snapshot.nTotal;
|
||||||
snapshot.handler.nRemaining = snapshot.nRemaining;
|
snapshot.handler.nRemaining = snapshot.nRemaining;
|
||||||
@ -330,13 +320,12 @@ export class TrialHandler extends PsychObject
|
|||||||
|
|
||||||
snapshot.handler.thisTrial = snapshot.handler.getCurrentTrial();
|
snapshot.handler.thisTrial = snapshot.handler.getCurrentTrial();
|
||||||
|
|
||||||
|
|
||||||
// add the snapshot's trial attributes to a global variable, whose name is derived from
|
// add the snapshot's trial attributes to a global variable, whose name is derived from
|
||||||
// that of the handler: loops -> thisLoop (note the dropped s):
|
// that of the handler: loops -> thisLoop (note the dropped s):
|
||||||
let name = snapshot.name;
|
let name = snapshot.name;
|
||||||
if (name[name.length-1] === 's')
|
if (name[name.length - 1] === "s")
|
||||||
{
|
{
|
||||||
name = name.substr(0, name.length-1);
|
name = name.substr(0, name.length - 1);
|
||||||
}
|
}
|
||||||
name = `this${name[0].toUpperCase()}${name.substr(1)}`;
|
name = `this${name[0].toUpperCase()}${name.substr(1)}`;
|
||||||
|
|
||||||
@ -348,7 +337,6 @@ export class TrialHandler extends PsychObject
|
|||||||
window[name] = value;
|
window[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for the finished attribute.
|
* Getter for the finished attribute.
|
||||||
*
|
*
|
||||||
@ -359,7 +347,6 @@ export class TrialHandler extends PsychObject
|
|||||||
return this._finished;
|
return this._finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setter for the finished attribute.
|
* Setter for the finished attribute.
|
||||||
*
|
*
|
||||||
@ -368,14 +355,13 @@ export class TrialHandler extends PsychObject
|
|||||||
set finished(isFinished)
|
set finished(isFinished)
|
||||||
{
|
{
|
||||||
this._finished = isFinished;
|
this._finished = isFinished;
|
||||||
|
|
||||||
this._snapshots.forEach( snapshot =>
|
this._snapshots.forEach((snapshot) =>
|
||||||
{
|
{
|
||||||
snapshot.finished = isFinished;
|
snapshot.finished = isFinished;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the trial index.
|
* Get the trial index.
|
||||||
*
|
*
|
||||||
@ -387,7 +373,6 @@ export class TrialHandler extends PsychObject
|
|||||||
return this.thisIndex;
|
return this.thisIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the trial index.
|
* Set the trial index.
|
||||||
*
|
*
|
||||||
@ -398,7 +383,6 @@ export class TrialHandler extends PsychObject
|
|||||||
this.thisIndex = index;
|
this.thisIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the attributes of the trials.
|
* Get the attributes of the trials.
|
||||||
*
|
*
|
||||||
@ -424,7 +408,6 @@ export class TrialHandler extends PsychObject
|
|||||||
return Object.keys(this.trialList[0]);
|
return Object.keys(this.trialList[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current trial.
|
* Get the current trial.
|
||||||
*
|
*
|
||||||
@ -436,7 +419,6 @@ export class TrialHandler extends PsychObject
|
|||||||
return this.trialList[this.thisIndex];
|
return this.trialList[this.thisIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the nth trial.
|
* Get the nth trial.
|
||||||
*
|
*
|
||||||
@ -453,7 +435,6 @@ export class TrialHandler extends PsychObject
|
|||||||
return this.trialList[index];
|
return this.trialList[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the nth future or past trial, without advancing through the trial list.
|
* Get the nth future or past trial, without advancing through the trial list.
|
||||||
*
|
*
|
||||||
@ -472,7 +453,6 @@ export class TrialHandler extends PsychObject
|
|||||||
return this.trialList[this.thisIndex + n];
|
return this.trialList[this.thisIndex + n];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the nth previous trial.
|
* Get the nth previous trial.
|
||||||
* <p> Note: this is useful for comparisons in n-back tasks.</p>
|
* <p> Note: this is useful for comparisons in n-back tasks.</p>
|
||||||
@ -486,7 +466,6 @@ export class TrialHandler extends PsychObject
|
|||||||
return getFutureTrial(-abs(n));
|
return getFutureTrial(-abs(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a key/value pair to data about the current trial held by the experiment handler
|
* Add a key/value pair to data about the current trial held by the experiment handler
|
||||||
*
|
*
|
||||||
@ -502,7 +481,6 @@ export class TrialHandler extends PsychObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import a list of conditions from a .xls, .xlsx, .odp, or .csv resource.
|
* Import a list of conditions from a .xls, .xlsx, .odp, or .csv resource.
|
||||||
*
|
*
|
||||||
@ -542,8 +520,8 @@ export class TrialHandler extends PsychObject
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
let resourceExtension = resourceName.split('.').pop();
|
let resourceExtension = resourceName.split(".").pop();
|
||||||
if (['csv', 'odp', 'xls', 'xlsx'].indexOf(resourceExtension) > -1)
|
if (["csv", "odp", "xls", "xlsx"].indexOf(resourceExtension) > -1)
|
||||||
{
|
{
|
||||||
// (*) read conditions from resource:
|
// (*) read conditions from resource:
|
||||||
const resourceValue = serverManager.getResource(resourceName, true);
|
const resourceValue = serverManager.getResource(resourceName, true);
|
||||||
@ -552,20 +530,20 @@ export class TrialHandler extends PsychObject
|
|||||||
// which is then read in as a string
|
// which is then read in as a string
|
||||||
const decodedResourceMaybe = new Uint8Array(resourceValue);
|
const decodedResourceMaybe = new Uint8Array(resourceValue);
|
||||||
// Could be set to 'buffer' for ASCII .csv
|
// Could be set to 'buffer' for ASCII .csv
|
||||||
const type = resourceExtension === 'csv' ? 'string' : 'array';
|
const type = resourceExtension === "csv" ? "string" : "array";
|
||||||
const decodedResource = type === 'string' ? (new TextDecoder()).decode(decodedResourceMaybe) : decodedResourceMaybe;
|
const decodedResource = type === "string" ? (new TextDecoder()).decode(decodedResourceMaybe) : decodedResourceMaybe;
|
||||||
const workbook = XLSX.read(decodedResource, { type });
|
const workbook = XLSX.read(decodedResource, { type });
|
||||||
|
|
||||||
// we consider only the first worksheet:
|
// we consider only the first worksheet:
|
||||||
if (workbook.SheetNames.length === 0)
|
if (workbook.SheetNames.length === 0)
|
||||||
{
|
{
|
||||||
throw 'workbook should contain at least one worksheet';
|
throw "workbook should contain at least one worksheet";
|
||||||
}
|
}
|
||||||
const sheetName = workbook.SheetNames[0];
|
const sheetName = workbook.SheetNames[0];
|
||||||
const worksheet = workbook.Sheets[sheetName];
|
const worksheet = workbook.Sheets[sheetName];
|
||||||
|
|
||||||
// worksheet to array of arrays (the first array contains the fields):
|
// worksheet to array of arrays (the first array contains the fields):
|
||||||
const sheet = XLSX.utils.sheet_to_json(worksheet, {header: 1, blankrows: false});
|
const sheet = XLSX.utils.sheet_to_json(worksheet, { header: 1, blankrows: false });
|
||||||
const fields = sheet.shift();
|
const fields = sheet.shift();
|
||||||
|
|
||||||
// (*) select conditions:
|
// (*) select conditions:
|
||||||
@ -574,8 +552,8 @@ export class TrialHandler extends PsychObject
|
|||||||
// (*) return the selected conditions as an array of 'object as map':
|
// (*) return the selected conditions as an array of 'object as map':
|
||||||
// [
|
// [
|
||||||
// {field0: value0-0, field1: value0-1, ...}
|
// {field0: value0-0, field1: value0-1, ...}
|
||||||
// {field0: value1-0, field1: value1-1, ...}
|
// {field0: value1-0, field1: value1-1, ...}
|
||||||
// ...
|
// ...
|
||||||
// ]
|
// ]
|
||||||
let trialList = new Array(selectedRows.length - 1);
|
let trialList = new Array(selectedRows.length - 1);
|
||||||
for (let r = 0; r < selectedRows.length; ++r)
|
for (let r = 0; r < selectedRows.length; ++r)
|
||||||
@ -598,7 +576,7 @@ export class TrialHandler extends PsychObject
|
|||||||
value = arrayMaybe;
|
value = arrayMaybe;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof value === 'string')
|
if (typeof value === "string")
|
||||||
{
|
{
|
||||||
const numberMaybe = Number.parseFloat(value);
|
const numberMaybe = Number.parseFloat(value);
|
||||||
|
|
||||||
@ -610,7 +588,7 @@ export class TrialHandler extends PsychObject
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Parse doubly escaped line feeds
|
// Parse doubly escaped line feeds
|
||||||
value = value.replace(/(\n)/g, '\n');
|
value = value.replace(/(\n)/g, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -621,23 +599,21 @@ export class TrialHandler extends PsychObject
|
|||||||
|
|
||||||
return trialList;
|
return trialList;
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw 'extension: ' + resourceExtension + ' currently not supported.';
|
throw "extension: " + resourceExtension + " currently not supported.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (error)
|
catch (error)
|
||||||
{
|
{
|
||||||
throw {
|
throw {
|
||||||
origin: 'TrialHandler.importConditions',
|
origin: "TrialHandler.importConditions",
|
||||||
context: `when importing condition: ${resourceName}`,
|
context: `when importing condition: ${resourceName}`,
|
||||||
error
|
error,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare the trial list.
|
* Prepare the trial list.
|
||||||
*
|
*
|
||||||
@ -647,16 +623,15 @@ export class TrialHandler extends PsychObject
|
|||||||
_prepareTrialList(trialList)
|
_prepareTrialList(trialList)
|
||||||
{
|
{
|
||||||
const response = {
|
const response = {
|
||||||
origin: 'TrialHandler._prepareTrialList',
|
origin: "TrialHandler._prepareTrialList",
|
||||||
context: 'when preparing the trial list'
|
context: "when preparing the trial list",
|
||||||
};
|
};
|
||||||
|
|
||||||
// we treat undefined trialList as a list with a single empty entry:
|
// we treat undefined trialList as a list with a single empty entry:
|
||||||
if (typeof trialList === 'undefined')
|
if (typeof trialList === "undefined")
|
||||||
{
|
{
|
||||||
this.trialList = [undefined];
|
this.trialList = [undefined];
|
||||||
}
|
}
|
||||||
|
|
||||||
// if trialList is an array, we make sure it is not empty:
|
// if trialList is an array, we make sure it is not empty:
|
||||||
else if (Array.isArray(trialList))
|
else if (Array.isArray(trialList))
|
||||||
{
|
{
|
||||||
@ -665,30 +640,27 @@ export class TrialHandler extends PsychObject
|
|||||||
this.trialList = [undefined];
|
this.trialList = [undefined];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if trialList is a string, we treat it as the name of the condition resource:
|
// if trialList is a string, we treat it as the name of the condition resource:
|
||||||
else if (typeof trialList === 'string')
|
else if (typeof trialList === "string")
|
||||||
{
|
{
|
||||||
this.trialList = TrialHandler.importConditions(this.psychoJS.serverManager, trialList);
|
this.trialList = TrialHandler.importConditions(this.psychoJS.serverManager, trialList);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unknown type:
|
// unknown type:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw Object.assign(response, {
|
throw Object.assign(response, {
|
||||||
error: 'unable to prepare trial list: unknown type: ' + (typeof trialList)
|
error: "unable to prepare trial list: unknown type: " + (typeof trialList),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare the sequence of trials.
|
* Prepare the sequence of trials.
|
||||||
*
|
*
|
||||||
* <p>The returned sequence is a matrix (an array of arrays) of trial indices
|
* <p>The returned sequence is a matrix (an array of arrays) of trial indices
|
||||||
* with nStim columns and nReps rows. Note that this is the transpose of the
|
* with nStim columns and nReps rows. Note that this is the transpose of the
|
||||||
* matrix return by PsychoPY.
|
* matrix return by PsychoPY.
|
||||||
*
|
*
|
||||||
* Example: with 3 trial and 5 repetitions, we get:
|
* Example: with 3 trial and 5 repetitions, we get:
|
||||||
* - sequential:
|
* - sequential:
|
||||||
* [[0 1 2]
|
* [[0 1 2]
|
||||||
@ -711,8 +683,8 @@ export class TrialHandler extends PsychObject
|
|||||||
_prepareSequence()
|
_prepareSequence()
|
||||||
{
|
{
|
||||||
const response = {
|
const response = {
|
||||||
origin: 'TrialHandler._prepareSequence',
|
origin: "TrialHandler._prepareSequence",
|
||||||
context: 'when preparing a sequence of trials'
|
context: "when preparing a sequence of trials",
|
||||||
};
|
};
|
||||||
|
|
||||||
// get an array of the indices of the elements of trialList :
|
// get an array of the indices of the elements of trialList :
|
||||||
@ -722,9 +694,8 @@ export class TrialHandler extends PsychObject
|
|||||||
{
|
{
|
||||||
this._trialSequence = Array(this.nReps).fill(indices);
|
this._trialSequence = Array(this.nReps).fill(indices);
|
||||||
// transposed version:
|
// transposed version:
|
||||||
//this._trialSequence = indices.reduce( (seq, e) => { seq.push( Array(this.nReps).fill(e) ); return seq; }, [] );
|
// this._trialSequence = indices.reduce( (seq, e) => { seq.push( Array(this.nReps).fill(e) ); return seq; }, [] );
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (this.method === TrialHandler.Method.RANDOM)
|
else if (this.method === TrialHandler.Method.RANDOM)
|
||||||
{
|
{
|
||||||
this._trialSequence = [];
|
this._trialSequence = [];
|
||||||
@ -733,7 +704,6 @@ export class TrialHandler extends PsychObject
|
|||||||
this._trialSequence.push(util.shuffle(indices.slice(), this._randomNumberGenerator));
|
this._trialSequence.push(util.shuffle(indices.slice(), this._randomNumberGenerator));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (this.method === TrialHandler.Method.FULL_RANDOM)
|
else if (this.method === TrialHandler.Method.FULL_RANDOM)
|
||||||
{
|
{
|
||||||
// create a flat sequence with nReps repeats of indices:
|
// create a flat sequence with nReps repeats of indices:
|
||||||
@ -755,15 +725,13 @@ export class TrialHandler extends PsychObject
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw Object.assign(response, {error: 'unknown method'});
|
throw Object.assign(response, { error: "unknown method" });
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._trialSequence;
|
return this._trialSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TrialHandler method
|
* TrialHandler method
|
||||||
*
|
*
|
||||||
@ -775,20 +743,20 @@ TrialHandler.Method = {
|
|||||||
/**
|
/**
|
||||||
* Conditions are presented in the order they are given.
|
* Conditions are presented in the order they are given.
|
||||||
*/
|
*/
|
||||||
SEQUENTIAL: Symbol.for('SEQUENTIAL'),
|
SEQUENTIAL: Symbol.for("SEQUENTIAL"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conditions are shuffled within each repeat.
|
* Conditions are shuffled within each repeat.
|
||||||
*/
|
*/
|
||||||
RANDOM: Symbol.for('RANDOM'),
|
RANDOM: Symbol.for("RANDOM"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conditions are fully randomised across all repeats.
|
* Conditions are fully randomised across all repeats.
|
||||||
*/
|
*/
|
||||||
FULL_RANDOM: Symbol.for('FULL_RANDOM'),
|
FULL_RANDOM: Symbol.for("FULL_RANDOM"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as above, but named to reflect PsychoPy boileplate.
|
* Same as above, but named to reflect PsychoPy boileplate.
|
||||||
*/
|
*/
|
||||||
FULLRANDOM: Symbol.for('FULL_RANDOM')
|
FULLRANDOM: Symbol.for("FULL_RANDOM"),
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export * from './ExperimentHandler.js';
|
export * from "./ExperimentHandler.js";
|
||||||
export * from './TrialHandler.js';
|
export * from "./TrialHandler.js";
|
||||||
//export * from './Shelf.js';
|
// export * from './Shelf.js';
|
||||||
|
Loading…
Reference in New Issue
Block a user