mirror of
https://github.com/psychopy/psychojs.git
synced 2025-05-10 18:50:54 +00:00
FF completed the information available in the experiment data for MultiStairHandler
This commit is contained in:
parent
3424b24b43
commit
86dee66770
@ -143,7 +143,7 @@ export class ServerManager extends PsychObject
|
|||||||
{
|
{
|
||||||
const url = this._psychoJS.config.pavlovia.URL
|
const url = this._psychoJS.config.pavlovia.URL
|
||||||
+ "/api/v2/experiments/" + this._psychoJS.config.gitlab.projectId
|
+ "/api/v2/experiments/" + this._psychoJS.config.gitlab.projectId
|
||||||
+ "/sessions/";
|
+ "/sessions";
|
||||||
jQuery.post(url, data, null, "json")
|
jQuery.post(url, data, null, "json")
|
||||||
.done((data, textStatus) =>
|
.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
|
* @name module:core.ServerManager#getResourceStatus
|
||||||
* @function
|
* @function
|
||||||
* @public
|
* @public
|
||||||
* @param {string} name of the requested resource
|
* @param {string | string[]} names names of the resources whose statuses are requested
|
||||||
* @return {core.ServerManager.ResourceStatus} status of the resource
|
* @return {core.ServerManager.ResourceStatus} status of the resource if there is only one, or reduced status otherwise
|
||||||
* @throws {Object.<string, *>} exception if no resource with that name has previously been registered
|
* @throws {Object.<string, *>} if at least one of the names is not that of a previously
|
||||||
|
* registered resource
|
||||||
*/
|
*/
|
||||||
getResourceStatus(name)
|
getResourceStatus(names)
|
||||||
{
|
{
|
||||||
const response = {
|
const response = {
|
||||||
origin: "ServerManager.getResourceStatus",
|
origin: "ServerManager.getResourceStatus",
|
||||||
context: "when getting the status of resource: " + name,
|
context: `when getting the status of resource(s): ${JSON.stringify(names)}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// sanity checks:
|
||||||
|
if (typeof names === 'string')
|
||||||
|
{
|
||||||
|
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);
|
const pathStatusData = this._resources.get(name);
|
||||||
|
|
||||||
if (typeof pathStatusData === "undefined")
|
if (typeof pathStatusData === "undefined")
|
||||||
{
|
{
|
||||||
// throw { ...response, error: 'unknown resource' };
|
// throw { ...response, error: 'unknown resource' };
|
||||||
throw Object.assign(response, { error: "unknown resource" });
|
throw Object.assign(response, {
|
||||||
|
error: `unable to find a previously registered resource with name: ${name}`
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathStatusData.status;
|
// 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 reducedStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@ -410,7 +452,7 @@ export class ServerManager extends PsychObject
|
|||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @name module:core.ServerManager#prepareResources
|
* @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
|
* @function
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -430,9 +472,13 @@ export class ServerManager extends PsychObject
|
|||||||
// register the resources:
|
// register the resources:
|
||||||
if (resources !== null)
|
if (resources !== null)
|
||||||
{
|
{
|
||||||
|
if (typeof resources === "string")
|
||||||
|
{
|
||||||
|
resources = [resources];
|
||||||
|
}
|
||||||
if (!Array.isArray(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:
|
// whether all resources have been requested:
|
||||||
@ -524,6 +570,12 @@ export class ServerManager extends PsychObject
|
|||||||
|
|
||||||
// download those registered resources for which download = true
|
// download those registered resources for which download = true
|
||||||
// note: we return a Promise that will be resolved when all the resources are downloaded
|
// note: we return a Promise that will be resolved when all the resources are downloaded
|
||||||
|
if (resourcesToDownload.size === 0)
|
||||||
|
{
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return new Promise((resolve, reject) =>
|
return new Promise((resolve, reject) =>
|
||||||
{
|
{
|
||||||
const uuid = this.on(ServerManager.Event.RESOURCE, (signal) =>
|
const uuid = this.on(ServerManager.Event.RESOURCE, (signal) =>
|
||||||
@ -538,6 +590,7 @@ export class ServerManager extends PsychObject
|
|||||||
this._downloadResources(resourcesToDownload);
|
this._downloadResources(resourcesToDownload);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (error)
|
catch (error)
|
||||||
{
|
{
|
||||||
console.error("error", error);
|
console.error("error", error);
|
||||||
@ -1357,6 +1410,11 @@ ServerManager.Status = {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
ServerManager.ResourceStatus = {
|
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.
|
* The resource has been registered.
|
||||||
*/
|
*/
|
||||||
@ -1371,9 +1429,4 @@ ServerManager.ResourceStatus = {
|
|||||||
* The resource has been downloaded.
|
* The resource has been downloaded.
|
||||||
*/
|
*/
|
||||||
DOWNLOADED: Symbol.for("DOWNLOADED"),
|
DOWNLOADED: Symbol.for("DOWNLOADED"),
|
||||||
|
|
||||||
/**
|
|
||||||
* There was an error during downloading, or the resource is in an unknown state.
|
|
||||||
*/
|
|
||||||
ERROR: Symbol.for("ERROR"),
|
|
||||||
};
|
};
|
||||||
|
@ -115,6 +115,7 @@ export class ExperimentHandler extends PsychObject
|
|||||||
* @function
|
* @function
|
||||||
* @public
|
* @public
|
||||||
* @returns {boolean} whether or not the current entry is empty
|
* @returns {boolean} whether or not the current entry is empty
|
||||||
|
* @todo This really should be renamed: IsCurrentEntryNotEmpty
|
||||||
*/
|
*/
|
||||||
isEntryEmpty()
|
isEntryEmpty()
|
||||||
{
|
{
|
||||||
|
@ -111,10 +111,12 @@ export class MultiStairHandler extends TrialHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._psychoJS.experiment.addData(this._name+'.response', response);
|
||||||
|
|
||||||
if (!this._finished)
|
if (!this._finished)
|
||||||
{
|
{
|
||||||
// update the current staircase:
|
// update the current staircase, but do not add the response again:
|
||||||
this._currentStaircase.addResponse(response, value);
|
this._currentStaircase.addResponse(response, value, false);
|
||||||
|
|
||||||
// move onto the next trial:
|
// move onto the next trial:
|
||||||
this._nextTrial();
|
this._nextTrial();
|
||||||
@ -206,6 +208,7 @@ export class MultiStairHandler extends TrialHandler
|
|||||||
const args = Object.assign({}, condition);
|
const args = Object.assign({}, condition);
|
||||||
args.psychoJS = this._psychoJS;
|
args.psychoJS = this._psychoJS;
|
||||||
args.varName = this._varName;
|
args.varName = this._varName;
|
||||||
|
// label becomes name:
|
||||||
args.name = condition.label;
|
args.name = condition.label;
|
||||||
args.autoLog = this._autoLog;
|
args.autoLog = this._autoLog;
|
||||||
if (typeof condition.nTrials === "undefined")
|
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 the current pass is empty, get a new one:
|
||||||
if (this._currentPass.length === 0)
|
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)
|
if (this._multiMethod === TrialHandler.Method.SEQUENTIAL)
|
||||||
{
|
{
|
||||||
@ -322,12 +325,48 @@ export class MultiStairHandler extends TrialHandler
|
|||||||
{
|
{
|
||||||
if (typeof this._trialList[t] === "undefined")
|
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")
|
if (typeof this._snapshots[t] !== "undefined")
|
||||||
{
|
{
|
||||||
this._snapshots[t][this._varName] = value;
|
let fieldName = this._name + "." + this._varName;
|
||||||
this._snapshots[t].trialAttributes.push(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;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -118,10 +118,12 @@ export class QuestHandler extends TrialHandler
|
|||||||
* @public
|
* @public
|
||||||
* @param{number} response - the response to the trial, must be either 0 (incorrect or
|
* @param{number} response - the response to the trial, must be either 0 (incorrect or
|
||||||
* non-detected) or 1 (correct or detected)
|
* 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}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
addResponse(response, value)
|
addResponse(response, value, doAddData = true)
|
||||||
{
|
{
|
||||||
// check that response is either 0 or 1:
|
// check that response is either 0 or 1:
|
||||||
if (response !== 0 && response !== 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:
|
// update the QUEST pdf:
|
||||||
if (typeof value !== "undefined")
|
if (typeof value !== "undefined")
|
||||||
{
|
{
|
||||||
@ -145,7 +152,10 @@ export class QuestHandler extends TrialHandler
|
|||||||
|
|
||||||
if (!this._finished)
|
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();
|
this._estimateQuestValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -688,7 +688,7 @@ export class TrialHandler extends PsychObject
|
|||||||
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:
|
||||||
const indices = Array.from(this.trialList.keys());
|
const indices = Array.from(this.trialList.keys());
|
||||||
|
|
||||||
if (this._method === TrialHandler.Method.SEQUENTIAL)
|
if (this._method === TrialHandler.Method.SEQUENTIAL)
|
||||||
|
Loading…
Reference in New Issue
Block a user