1
0
mirror of https://github.com/psychopy/psychojs.git synced 2025-05-10 10:40:54 +00:00

FF completed the information available in the experiment data for MultiStairHandler

This commit is contained in:
Alain Pitiot 2022-02-11 11:01:13 +01:00
parent 3424b24b43
commit 86dee66770
5 changed files with 141 additions and 38 deletions

View File

@ -143,7 +143,7 @@ export class ServerManager extends PsychObject
{
const url = this._psychoJS.config.pavlovia.URL
+ "/api/v2/experiments/" + this._psychoJS.config.gitlab.projectId
+ "/sessions/";
+ "/sessions";
jQuery.post(url, data, null, "json")
.done((data, textStatus) =>
{
@ -324,30 +324,72 @@ export class ServerManager extends PsychObject
}
/****************************************************************************
* Get the status of a resource.
* Get the status of a single resource or the reduced status of an array of resources.
*
* <p>If an array of resources is given, getResourceStatus returns a single, reduced status
* that is the status furthest away from DOWNLOADED, with the status ordered as follow:
* ERROR (furthest from DOWNLOADED), REGISTERED, DOWNLOADING, and DOWNLOADED</p>
* <p>For example, given three resources:
* <ul>
* <li>if at least one of the resource status is ERROR, the reduced status is ERROR</li>
* <li>if at least one of the resource status is DOWNLOADING, the reduced status is DOWNLOADING</li>
* <li>if the status of all three resources is REGISTERED, the reduced status is REGISTERED</li>
* <li>if the status of all three resources is DOWNLOADED, the reduced status is DOWNLOADED</li>
* </ul>
* </p>
*
* @name module:core.ServerManager#getResourceStatus
* @function
* @public
* @param {string} name of the requested resource
* @return {core.ServerManager.ResourceStatus} status of the resource
* @throws {Object.<string, *>} exception if no resource with that name has previously been registered
* @param {string | string[]} names names of the resources whose statuses are requested
* @return {core.ServerManager.ResourceStatus} status of the resource if there is only one, or reduced status otherwise
* @throws {Object.<string, *>} if at least one of the names is not that of a previously
* registered resource
*/
getResourceStatus(name)
getResourceStatus(names)
{
const response = {
origin: "ServerManager.getResourceStatus",
context: "when getting the status of resource: " + name,
context: `when getting the status of resource(s): ${JSON.stringify(names)}`,
};
const pathStatusData = this._resources.get(name);
if (typeof pathStatusData === "undefined")
// sanity checks:
if (typeof names === 'string')
{
// throw { ...response, error: 'unknown resource' };
throw Object.assign(response, { error: "unknown resource" });
names = [names];
}
if (!Array.isArray(names))
{
throw Object.assign(response, { error: "names should be either a string or an array of strings" });
}
const statusOrder = new Map([
[Symbol.keyFor(ServerManager.ResourceStatus.ERROR), 0],
[Symbol.keyFor(ServerManager.ResourceStatus.REGISTERED), 1],
[Symbol.keyFor(ServerManager.ResourceStatus.DOWNLOADING), 2],
[Symbol.keyFor(ServerManager.ResourceStatus.DOWNLOADED), 3]
]);
let reducedStatus = ServerManager.ResourceStatus.DOWNLOADED;
for (const name of names)
{
const pathStatusData = this._resources.get(name);
if (typeof pathStatusData === "undefined")
{
// throw { ...response, error: 'unknown resource' };
throw Object.assign(response, {
error: `unable to find a previously registered resource with name: ${name}`
});
}
// update the reduced status according to the order given by statusOrder:
if (statusOrder.get(Symbol.keyFor(pathStatusData.status)) <
statusOrder.get(Symbol.keyFor(reducedStatus)))
{
reducedStatus = pathStatusData.status;
}
}
return pathStatusData.status;
return reducedStatus;
}
/****************************************************************************
@ -410,7 +452,7 @@ export class ServerManager extends PsychObject
* </ul>
*
* @name module:core.ServerManager#prepareResources
* @param {Array.<{name: string, path: string, download: boolean} | String | Symbol>} [resources=[]] - the list of resources
* @param {String | Array.<{name: string, path: string, download: boolean} | String | Symbol>} [resources=[]] - the list of resources or a single resource
* @function
* @public
*/
@ -430,9 +472,13 @@ export class ServerManager extends PsychObject
// register the resources:
if (resources !== null)
{
if (typeof resources === "string")
{
resources = [resources];
}
if (!Array.isArray(resources))
{
throw "resources should be an array of string or objects";
throw "resources should be either (a) a string or (b) an array of string or objects";
}
// whether all resources have been requested:
@ -524,19 +570,26 @@ export class ServerManager extends PsychObject
// download those registered resources for which download = true
// note: we return a Promise that will be resolved when all the resources are downloaded
return new Promise((resolve, reject) =>
if (resourcesToDownload.size === 0)
{
const uuid = this.on(ServerManager.Event.RESOURCE, (signal) =>
return Promise.resolve();
}
else
{
return new Promise((resolve, reject) =>
{
if (signal.message === ServerManager.Event.DOWNLOAD_COMPLETED)
const uuid = this.on(ServerManager.Event.RESOURCE, (signal) =>
{
this.off(ServerManager.Event.RESOURCE, uuid);
resolve();
}
});
if (signal.message === ServerManager.Event.DOWNLOAD_COMPLETED)
{
this.off(ServerManager.Event.RESOURCE, uuid);
resolve();
}
});
this._downloadResources(resourcesToDownload);
});
this._downloadResources(resourcesToDownload);
});
}
}
catch (error)
{
@ -1357,6 +1410,11 @@ ServerManager.Status = {
* @public
*/
ServerManager.ResourceStatus = {
/**
* There was an error during downloading, or the resource is in an unknown state.
*/
ERROR: Symbol.for("ERROR"),
/**
* The resource has been registered.
*/
@ -1371,9 +1429,4 @@ ServerManager.ResourceStatus = {
* The resource has been downloaded.
*/
DOWNLOADED: Symbol.for("DOWNLOADED"),
/**
* There was an error during downloading, or the resource is in an unknown state.
*/
ERROR: Symbol.for("ERROR"),
};

View File

@ -115,6 +115,7 @@ export class ExperimentHandler extends PsychObject
* @function
* @public
* @returns {boolean} whether or not the current entry is empty
* @todo This really should be renamed: IsCurrentEntryNotEmpty
*/
isEntryEmpty()
{

View File

@ -111,10 +111,12 @@ export class MultiStairHandler extends TrialHandler
};
}
this._psychoJS.experiment.addData(this._name+'.response', response);
if (!this._finished)
{
// update the current staircase:
this._currentStaircase.addResponse(response, value);
// update the current staircase, but do not add the response again:
this._currentStaircase.addResponse(response, value, false);
// move onto the next trial:
this._nextTrial();
@ -206,6 +208,7 @@ export class MultiStairHandler extends TrialHandler
const args = Object.assign({}, condition);
args.psychoJS = this._psychoJS;
args.varName = this._varName;
// label becomes name:
args.name = condition.label;
args.autoLog = this._autoLog;
if (typeof condition.nTrials === "undefined")
@ -254,7 +257,7 @@ export class MultiStairHandler extends TrialHandler
// if the current pass is empty, get a new one:
if (this._currentPass.length === 0)
{
this._currentPass = this._staircases.filter(handler => !handler.finished);
this._currentPass = this._staircases.filter( handler => !handler.finished );
if (this._multiMethod === TrialHandler.Method.SEQUENTIAL)
{
@ -322,12 +325,48 @@ export class MultiStairHandler extends TrialHandler
{
if (typeof this._trialList[t] === "undefined")
{
this._trialList[t] = {[this._varName]: value};
this._trialList[t] = {
[this._name+"."+this._varName]: value,
[this._name+".intensity"]: value
};
for (const attribute of this._currentStaircase._userAttributes)
{
// "name" becomes "label" again:
if (attribute === "name")
{
this._trialList[t][this._name+".label"] = this._currentStaircase["_name"];
}
else if (attribute !== "trialList" && attribute !== "extraInfo")
{
this._trialList[t][this._name+"."+attribute] = this._currentStaircase["_" + attribute];
}
}
if (typeof this._snapshots[t] !== "undefined")
{
this._snapshots[t][this._varName] = value;
this._snapshots[t].trialAttributes.push(this._varName);
let fieldName = this._name + "." + this._varName;
this._snapshots[t][fieldName] = value;
this._snapshots[t].trialAttributes.push(fieldName);
fieldName = this._name + ".intensity";
this._snapshots[t][fieldName] = value;
this._snapshots[t].trialAttributes.push(fieldName);
for (const attribute of this._currentStaircase._userAttributes)
{
// "name" becomes "label" again:
if (attribute === 'name')
{
fieldName = this._name + ".label";
this._snapshots[t][fieldName] = this._currentStaircase["_name"];
this._snapshots[t].trialAttributes.push(fieldName);
}
else if (attribute !== 'trialList' && attribute !== 'extraInfo')
{
fieldName = this._name+"."+attribute;
this._snapshots[t][fieldName] = this._currentStaircase["_" + attribute];
this._snapshots[t].trialAttributes.push(fieldName);
}
}
}
break;
}

View File

@ -118,10 +118,12 @@ export class QuestHandler extends TrialHandler
* @public
* @param{number} response - the response to the trial, must be either 0 (incorrect or
* non-detected) or 1 (correct or detected)
* @param{number | undefined} [value] - optional intensity / contrast / threshold
* @param{number | undefined} value - optional intensity / contrast / threshold
* @param{boolean} [doAddData = true] - whether or not to add the response as data to the
* experiment
* @returns {void}
*/
addResponse(response, value)
addResponse(response, value, doAddData = true)
{
// check that response is either 0 or 1:
if (response !== 0 && response !== 1)
@ -133,6 +135,11 @@ export class QuestHandler extends TrialHandler
};
}
if (doAddData)
{
this._psychoJS.experiment.addData(this._name + '.response', response);
}
// update the QUEST pdf:
if (typeof value !== "undefined")
{
@ -145,7 +152,10 @@ export class QuestHandler extends TrialHandler
if (!this._finished)
{
// estimate the next value of the QUEST variable (and update the trial list and snapshots):
this.next();
// estimate the next value of the QUEST variable
// (and update the trial list and snapshots):
this._estimateQuestValue();
}
}

View File

@ -688,7 +688,7 @@ export class TrialHandler extends PsychObject
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:
const indices = Array.from(this.trialList.keys());
if (this._method === TrialHandler.Method.SEQUENTIAL)