Home Reference Source

scripts/experiment-properties/data/data_controller.js

import {CUSTOM_TRIAL_STRUCTURE_CONDITIONS, get_subconditions} from "/scripts/experiment-properties/data/custom_subcondition_generator.js";
import {JND_BASE, JND_CONDITIONS} from "/scripts/experiment-properties/data/constants/jnd_data.js";
import {STEVENS_BASE, STEVENS_CONDITIONS} from "/scripts/experiment-properties/data/constants/stevens_data.js";
import {ESTIMATION_CONDITIONS} from "/scripts/experiment-properties/data/constants/estimation_data.js";
import {JND_RADIUS_BASE, JND_RADIUS_CONDITIONS} from "/scripts/experiment-properties/data/constants/jnd_radius_data.js";
import {NUMEROSITY_BASE, NUMEROSITY_CONDITIONS} from "/scripts/experiment-properties/data/constants/numerosity_data.js";
import {VISUAL_SEARCH_BASE, VISUAL_SEARCH_CONDITIONS} from "/scripts/experiment-properties/data/constants/visual_search_data.js";

export { get_data,
         get_data_subset,
         create_condition_dataset,
         EXPERIMENT_BASES,
         EXPERIMENT_CONDITIONS };

const EXPERIMENT_BASES = {
  "JND" : JND_BASE,
  "Stevens" : STEVENS_BASE,
  "JND_Radius" : JND_RADIUS_BASE,
  "Estimation" : [],
  "Numerosity" : NUMEROSITY_BASE,
  "Visual_Search" : VISUAL_SEARCH_BASE
};

const EXPERIMENT_CONDITIONS = {
  "JND" : JND_CONDITIONS,
  "Stevens" : STEVENS_CONDITIONS,
  "JND_Radius" : JND_RADIUS_CONDITIONS,
  "Estimation" : ESTIMATION_CONDITIONS,
  "Numerosity" : NUMEROSITY_CONDITIONS,
  "Visual_Search" : VISUAL_SEARCH_CONDITIONS,
};

/**
 * Retrieves the data for the corresponding experiment object.
 *
 * @param  experiment  {object}       Model object of the experiment 
 *
 * @return dataset     [{assoc}, {assoc}, .... ]         
 */
function get_data(experiment){
  var dataset;

  var trial_structure = experiment.trial_structure;
  var graph_type = experiment.graph_type;
  var condition = experiment.condition_name;
  var experiment_name = experiment.constructor.name;

  // Estimation does not have any BASE
  if (experiment_name === "Estimation"){
    dataset = EXPERIMENT_CONDITIONS["Estimation"][condition];
    console.log("LENGTH: " + dataset.length);
  }
  // If there is a "custom" condition AKA the subconditions are 
  // generated by code rather than by constants (arrays in the constants folder)
  else if (trial_structure === "custom" || CUSTOM_TRIAL_STRUCTURE_CONDITIONS[trial_structure].includes(condition)) {

    dataset = get_subconditions(experiment);
  }
  else {

    // If not a base condition
    if (!condition.split("_").includes("base")){
      // Check that a condition exists
      if (!EXPERIMENT_CONDITIONS[experiment_name][condition] || !EXPERIMENT_BASES[experiment_name][trial_structure]){
        throw new Error (condition + " is not supported.");
      }

      // Get subconditions then append to the base 
      let subconditions = EXPERIMENT_CONDITIONS[experiment_name][condition];
      dataset = create_condition_dataset(EXPERIMENT_BASES[experiment_name][trial_structure], subconditions);

    // If a base condition
    } else {

      if (!EXPERIMENT_BASES[experiment_name][trial_structure]) {
        throw new Error ("Base for " + condition + " does not exist.");
      }

      dataset = EXPERIMENT_BASES[experiment_name][trial_structure];
    }
  }

  check_dataset(experiment_name, graph_type, dataset)

  return dataset;
}

/**
 * Checks if all subconditions have the necessary attributes to run the experiment.
 *
 * @param  experiment      {string}                 type of experiment
 *         graph_type.     {string}                 type of graph
 *         dataset         {array of JS objects}    dataset to check            
 */
function check_dataset(experiment_name, graph_type, dataset) {

  let name = "";
  let graph_attributes = GRAPH_TYPES[graph_type]["attributes"];
  let exp_attributes = EXPERIMENTS[experiment_name.toLowerCase()]["attributes"];
  let all_attributes = Object.assign({}, graph_attributes);
  all_attributes = Object.assign(all_attributes, exp_attributes);

  for (let subcondition of dataset) {

    // Check that each subcond has the necessary attributes to run the experiment
    for (let attribute_key in EXPERIMENTS[experiment_name.toLowerCase()]["attributes"]){

      let attribute = EXPERIMENTS[experiment_name.toLowerCase()]["attributes"][attribute_key];

      // Check that conditions has all required attributes
      if (subcondition[attribute_key] === undefined && attribute["required"]){
        throw new Error("Required attribute " + attribute_key + " was not found in the subcondition " + JSON.stringify(subcondition));
      }
    }

    for (let subcondition_key in subcondition) {

      // Check that all attributes have been defined in either experiment or graphing config
      if (all_attributes[subcondition_key] === undefined) {
        throw new Error("Attribute " + subcondition_key + " has not been defined in config files.");
      }

      // Check that if there is a valid_input, it is listed in the configs
      if (Array.isArray(subcondition[subcondition_key])) { //If array, loop through values and check each

        for (let value of subcondition[subcondition_key]) {
          if (all_attributes[subcondition_key].valid_inputs 
            && !all_attributes[subcondition_key].valid_inputs.includes(value)){
            throw new Error("Attribute " + subcondition_key + " has value " + value + " that is not a valid value. Valid values are: " + all_attributes[subcondition_key].valid_inputs);
          }
        }

      } else {
        if (all_attributes[subcondition_key].valid_inputs 
          && !all_attributes[subcondition_key].valid_inputs.includes(subcondition[subcondition_key])){
          throw new Error("Attribute " + subcondition_key + " has value " + subcondition[subcondition_key] + " that is not a valid value. Valid values are: " + all_attributes[subcondition_key].valid_inputs);
        }
      }
    }
  }
}

/**
 * Retrieves a smaller dataset (4 subconditions) given experiment, trial structure and condition.
 *
 * @param  experiment      {string}            "jnd", "stevens", etc.  
 *         trial_structure {string}            "foundational", "design", etc.      
 *         condition       {string}            Name of condition
 *
 * @return dataset     [{assoc}, {assoc}, .... ]         
 */
function get_data_subset(experiment, trial_structure, condition) {

  var dataset = get_data(experiment, trial_structure, condition);

  return dataset.slice(0, 4);
}

/**
 * Appends condition-specific data to the dataset.
 *
 * @param  base_data         [{assoc}, {assoc}, .... ]     dataset with base experiment constants   
 * @param  condition_data    [{assoc}, {assoc}, .... ]     condition set for that experiment
 *
 * @return dataset           [{assoc}, {assoc}, .... ]  
 **/
function create_condition_dataset(base_data, condition_data){
  var condition_dataset = [];

  if (base_data.length != condition_data.length) {
    throw Error("Base dataset length is not equal to condition dataset length.");
  }

  for (let i in base_data) {
    let obj = Object.assign({}, base_data[i], condition_data[i]);
    condition_dataset.push(obj);
  }

  return condition_dataset;
}