ROAR DocumentationROAR Documentation
  • Databases
  • Workflows
  • Application
  • GitHub Actions
  • Dashboard Components
  • Firebase App Check
  • Cloud Functions
  • Backend Architecture
  • Internationalization
  • Integrating New Apps
  • Optimizing Assets
  • ROAR Redivis Instance
  • Logging and Querying
  • Emulation
  • Data Guidelines
  • Data Organization
  • Data Requests
GitHub
  • Databases
  • Workflows
  • Application
  • GitHub Actions
  • Dashboard Components
  • Firebase App Check
  • Cloud Functions
  • Backend Architecture
  • Internationalization
  • Integrating New Apps
  • Optimizing Assets
  • ROAR Redivis Instance
  • Logging and Querying
  • Emulation
  • Data Guidelines
  • Data Organization
  • Data Requests
GitHub
  • Databases
    • Database Information
    • gse-roar-admin
    • gse-roar-assessment
  • BigQuery
    • Querying Assessment Data
    • BigQuery schema: classes
    • BigQuery schema: districts
    • BigQuery schema: families
    • BigQuery schema: groups
    • BigQuery schema: schools
    • BigQuery schema: user_runs
    • BigQuery schema: user_trials
    • BigQuery schema: users
  • Workflows
    • Workflows
    • Creating an Assignment
    • Authentication
    • Creating new Users
    • User Roster Changes
    • How to Impersonate a Clever User on Localhost
  • Application

    • Auth
  • GitHub Actions
    • ROAR Apps GitHub Actions
      • GitHub Actions in ROAR Apps
      • firebase-deploy-preview.yml
      • firebase-hosting-merge.yml
      • publish-to-npm-create-new-release.yml
      • submit-dashboard-pr.yml
    • ROAR Dashboard GitHub Actions
      • GitHub Actions in the ROAR Dashboard
  • Dashboard Components
    • Dashboard Components
    • Organization Score Reports
  • Firebase App Check
    • Firebase App Check Configuration for roar-firekit and roar-dashboard
  • Backend Architecture
    • Architecture
      • Backend Architecture in ROAR
      • Data Models
      • Database Implementations
      • Error Handling Architecture in ROAR
      • Repository Layer Architecture
      • Service Layer Architecture
    • API
      • Classes
        • Class: AdministrationServiceError
        • Class: FirebaseClientError
        • Class: FirebaseImplementationError
        • Class: FirestoreAdministrationRepository
        • Class: FirestoreAdministrationRepositoryError
        • Class: abstract FirestoreBaseRepository<T>
        • Class: FirestoreFilterAdapter
        • Class: FirestoreIdentityProviderRepository
        • Class: FirestoreIdentityProviderRepositoryError
        • Class: FirestoreOrgRepository
        • Class: FirestoreOrgRepositoryError
        • Class: FirestoreRepositoryError
        • Class: FirestoreUserClaimRepository
        • Class: FirestoreUserClaimRepositoryError
        • Class: FirestoreUserRepository
        • Class: FirestoreUserRepositoryError
        • Class: IdentityProviderServiceError
        • Classes
      • Enumerations
        • Enumeration: CollectionType
        • Enumeration: IdentityProviderType
        • Enumeration: Operator
        • Enumerations
      • Functions
        • Functions
        • Function: chunkOrgs()
        • Function: createAdministrationService()
        • Function: createFirestoreImplementation()
        • Function: createIdentityProviderService()
        • Function: isEmptyOrgs()
      • Interfaces
        • Interface: Administration
        • Interface: AdministrationBaseRepository
        • Interface: AdministrationService
        • Interface: AssentConsent
        • Interface: Assessment
        • Interface: BaseModel
        • Interface: BaseRepository<T>
        • Interface: Claims
        • Interface: CompositeCondition
        • Interface: CompositeFilter
        • Interface: CreateAdministrationServiceParams<AdminRepo, OrgRepo, UserClaimRepo>
        • Interface: CreateParams
        • Interface: DeleteParams
        • Interface: EducationalOrgsList
        • Interface: FieldCondition
        • Interface: FilterAdapter<T>
        • Interface: FirestoreCreateParams
        • Interface: FirestoreDeleteParams
        • Interface: FirestoreFetchDocumentParams
        • Interface: FirestoreGetAllParams
        • Interface: FirestoreGetByIdParams
        • Interface: FirestoreGetByNameParams
        • Interface: FirestoreGetByRoarUidParams
        • Interface: FirestoreGetParams
        • Interface: FirestoreGetWithFiltersParams
        • Interface: FirestoreImplementation
        • Interface: FirestoreRunTransactionParams<T>
        • Interface: FirestoreUpdateParams
        • Interface: FutureParams
        • Interface: GetAdministrationIdsForAdministratorParams
        • Interface: GetAdministrationIdsFromOrgsParams
        • Interface: GetAllParams
        • Interface: GetByNameParams
        • Interface: GetByProviderIdParams
        • Interface: GetByRoarUidParams
        • Interface: GetParams
        • Interface: GetRoarUidParams
        • Interface: IdentityProvider
        • Interface: IdentityProviderBaseRepository
        • Interface: IdentityProviderService
        • Interface: Legal
        • Interface: OrgBase
        • Interface: OrgBaseRepository
        • Interface: OrgsList
        • Interfaces
        • Interface: Result<T>
        • Interface: RunTransactionParams<T>
        • Interface: SingleFilter
        • Interface: UpdateParams
        • Interface: User
        • Interface: UserBaseRepository
        • Interface: UserClaim
        • Interface: UserClaimBaseRepository
        • Interface: createIdentityProviderServiceParams<IDPRepo, UserClaimRepo, UserRepo>
        • Interface: getAdministrationIdsFromOrgsParams
        • Interface: _setAdministrationIdsParams
      • Type Aliases
        • Type Alias: BaseFilter
        • Type Alias: ComparisonOperator
        • Type Alias: Condition
        • Type Alias: DocumentCreatedEvent
        • Type Alias: DocumentDeletedEvent
        • Type Alias: DocumentUpdatedEvent
        • Type Alias: DocumentWrittenEvent
        • Type Alias: ParameterValue
        • Type Aliases
        • Type Alias: SelectAllCondition
      • Variables
        • Variable: FirebaseAppClient
        • Variable: FirebaseAuthClient
        • Variable: FirestoreClient
        • Variable: ORG_NAMES
        • Variables API Documentation
    • Examples
      • Examples
    • Guides
      • Guides
  • Cloud Functions
    • gse-roar-admin
      • Admin Database
      • appendToAdminClaims()
      • associateassessmentuid()
      • createAdministratorAccount()
      • createGuestDocsForGoogleUsers()
      • createLevanteGroup()
      • createLevanteUsers()
      • createnewfamily()
      • createstudentaccount()
      • mirrorClasses()
      • mirrorCustomClaims
      • mirrorDistricts()
      • mirrorFamilies()
      • mirrorGroups()
      • mirrorSchools()
      • removefromadminclaims()
      • saveSurveyResponses()
      • setuidcustomclaims()
      • softDeleteUserAssignment()
      • softDeleteUserExternalData
      • softDeleteUser()
      • syncAssignmentCreated()
      • syncAssignmentDeleted()
      • syncAssignmentUpdated()
      • syncAssignmentsOnAdministrationUpdate()
      • syncAssignmentsOnUserUpdate()
      • syncCleverOrgs()
      • syncCleverUser()
    • gse-roar-assessment
      • Assessment Database
      • organizeBucketLogsByDate()
      • setuidclaims()
      • softDeleteGuestTrial()
      • softDeleteGuest()
      • softDeleteUserRun()
      • softDeleteUserTrial()
      • syncOnRunDocUpdate()
  • Internationalization
    • ROAM Fluency
    • ROAR Letter
    • ROAR Phoneme
    • Internationalization of ROAR Apps
    • ROAR Sentence
    • ROAR Word
  • Integrating New Apps
    • Integrating Roar Apps into the Dashboard
    • Dashboard Integration
    • Monitoring and Testing
    • Preparing the App for Packaging and Deployment
    • Packaging and Publishing to npm
    • Secrets in the GitHub Repository
  • Assets Optimization
    • Optimizing Assets
    • Audio Optimization Guide
    • Image Optimization Guide
  • ROAR Redivis Instance
    • ROAR Redivis Instance
    • ROAR Data Validator Trigger
    • ROAR Data Validator
  • Logging and Querying
    • ROAR Logging
  • Emulation
    • Running the Emulator
      • Commands
    • Emulator Configuration Guide
      • Configuration
      • Cypress Configuration
      • Setup and Dependencies
      • Firebase CLI Configuration
      • Firebase Emulator Configuration
      • GitHub Secrets and Workflows
      • Importing and Exporting Data
      • Local Environment Variables
  • Clowder Implementation
    • Clowder Integration
    • Letter - Clowder
    • Multichoice - Clowder
    • Phoneme - Clowder
    • ARF & CALF - Clowder

Phoneme - Clowder

📝 NOTE: Phoneme will use the URL param adaptive=true to switch between adaptive and non-adaptive modes.

1. Parameter Extraction from URL (serve/serve.js)

  • Extract Clowder-specific parameters such as earlyStopping, tolerance, and logicalOperation.
  • Add these parameters to the gameParams array.
// Parameters for Clowder

const earlyStopping = urlParams.get('earlyStopping')?.toLowerCase() ?? null;
const tolerance = urlParams.get('tolerance') ?? null;
const logicalOperation = urlParams.get('logicalOperation')?.toLowerCase() ?? null;

// Other useful parameters

const threshold = urlParams.get('threshold') ?? null;
const patience = urlParams.get('patience') ?? null;
const nItems = urlParams.get('nItems') ? parseInt(urlParams.get('nItems'), 10) : null;
const randomSeed = urlParams.get('random') ?? null;
const catsToUpdate = urlParams.get('catsToUpdate')?.split(',') ?? [];

// Add more parameters for Clowder if needed

2. Initialize Clowder in the Store Session (experiment/config/config.js)

  • Inside the initStore function, set the following:
store.session.set('itemSelect', 'mfi'); // Maximum Fisher Information algorithm (modifiable if needed)
store.session.set('adaptive', config.adaptive);
store.session.set('previousItem', null);
store.session.set('previousAnswer', null);
store.session.set('currentCatIndex', null);
  • Initialize Clowder before returning the session:
initializeClowder();

3. Create CATs and Clowder (experiment/experimentHelpers.js)

  • Implement the initializeClowder function to set up Clowder instances for different phoneme-related trials.
  • Define catsConfig for each Clowder instance, specifying method, itemSelect, minTheta, maxTheta, and randomSeed.
  • Initialize Clowder’s corpus using prepareClowderCorpus.
  • Select the next stimulus using the Clowder function updateCatAndGetNextItem.
import { Cat, Clowder, StopAfterNItems, prepareClowderCorpus } from '@bdelab/jscat';

const catOrderMap = {
  0: 'practiceFSM',
  1: 'fsm',
  2: 'practiceLSM',
  3: 'lsm',
  4: 'practiceDEL',
  5: 'del',
};

// eslint-disable-next-line import/no-mutable-exports
export let clowder;

// TODO: Update values accordingly

export const initializeClowder = () => {
  // Define the `cats` configuration
  const catsConfig = {
    practiceFSM: {
      method: 'MLE',
      itemSelect: store.session('itemSelect'),
      minTheta: -3,
      maxTheta: 3,
      randomSeed: 'seed-fsm-practice',
    },
    fsm: {
      method: 'MLE',
      itemSelect: store.session('itemSelect'),
      minTheta: -3,
      maxTheta: 3,
      randomSeed: 'seed-fsm',
    },
    practiceLSM: {
      method: 'MLE',
      itemSelect: store.session('itemSelect'),
      minTheta: -3,
      maxTheta: 3,
      randomSeed: 'seed-lsm-practice',
    },
    lsm: {
      method: 'MLE',
      itemSelect: store.session('itemSelect'),
      minTheta: -3,
      maxTheta: 3,
      randomSeed: 'seed-lsm',
    },
    practiceDEL: {
      method: 'MLE',
      itemSelect: store.session('itemSelect'),
      minTheta: -3,
      maxTheta: 3,
      randomSeed: 'seed-del-practice',
    },
    del: {
      method: 'MLE',
      itemSelect: store.session('itemSelect'),
      minTheta: -3,
      maxTheta: 3,
      randomSeed: 'seed-del',
    },
  };

  // IF EARLY STOPPING IS REQUIRED

  // let earlyStoppingCats = null;

  // if (store.session.get('config').earlyStopping) {
  // const earlyStoppingCats = new StopAfterNItems({
  //   requiredItems: {
  //     fsm: 2,
  //     lsm: 2,
  //     del: 2,
  //   },
  //   logicalOperation: store.session.get('config').logicalOperation ?? 'only',
  // });
  // }

📝 NOTE: all columns must be defined in the corpus. if there is an undefined column, it will consider this stimulus as not-new and the previousItem will not properly be saved.

const combinedCorpus = [
  ...corpus.practice_DEL,
  ...corpus.practice_FSM,
  ...corpus.practice_LSM,
  ...corpus.test_DEL,
  ...corpus.test_FSM,
  ...corpus.test_LSM,
];

const clowderCorpus = prepareClowderCorpus(
  combinedCorpus,
  ['practiceFSM', 'practiceLSM', 'practiceDEL', 'fsm', 'lsm', 'del', 'total'],
  '.',
);

store.session.set('corpusClowder', clowderCorpus);

clowder = new Clowder({
  cats: catsConfig,
  corpus: clowderCorpus,
  randomSeed: store.session.get('config').randomSeed,
  // earlyStopping: earlyStoppingCats,
});
};

export const moveToNextBlock = () => {
const catIndex = (store.session.get('currentCatIndex') ?? -1) + 1;
store.session.set('currentCatIndex', catIndex);
};

export const setNextStimulus = () => {
let catIndex = store.session.get('currentCatIndex');
// eslint-disable-next-line eqeqeq
if (catIndex == undefined) {
  store.session.set('currentCatIndex', 0);
  catIndex = 0;
}

const catName = catOrderMap[catIndex];
const previousItem = store.session.get('previousItem');
const previousAnswer = store.session.get('previousAnswer');

const nextStimulus = clowder.updateCatAndGetNextItem({
  catToSelect: catName,
  catsToUpdate: store.session.get('config').catsToUpdate ?? [
    'practiceFSM',
    'fsm',
    'practiceLSM',
    'lsm',
    'practiceDEL',
    'del',
    'total',
  ],
  items: previousItem ?? undefined,
  answers: previousAnswer ?? undefined,
  randomlySelectUnvalidated: false,
});

if (nextStimulus === undefined) {
  store.session.remove('currentStimulus');
  moveToNextBlock();
} else {
  store.session.set('currentStimulus', nextStimulus);
}
};

  • On saveTrialData, don't forget to add the responses.
if (response === store.session('currentStimulus').goal) {
store.session.set('previousAnswer', 1);
store.session.set('previousItem', store.session.get('currentStimulus'));
} else {
store.session.set('previousAnswer', 0);
store.session.set('previousItem', store.session('currentStimulus'));
}

4. **Stimulus Control (experiment/trials/{fsm, lsm, del}/{instructions, ready, test}.js) **

  • import setNextStimulus from experiment/experimentHelpers
import { setNextStimulus } from '../experimentHelpers';
  • Call setNextStimulus every time the currentStimulus is set
if (store.session('config').adaptive) {
  store.session.set('currentStimulus', setNextStimulus());
} else {
  store.session.set('currentStimulus', corpus.practice_DEL[store.session('currentCorpusIndex')]);
}

5. setNextStimulus and saving responses (experiment/trials/test.js)

  • import setNextStimulus from experiment/experimentHelpers
import { setNextStimulus } from '../experimentHelpers';
  • Call setNextStimulus for the switch mode
switch (mode) {
    case 'practice':
        if (store.session.get('previousAnswer') !== 0 && store.session.get('config').adaptive) setNextStimulus();
        return mediaAssets.audio[camelize(store.session('currentStimulus').instr)];
    case 'del':
        if (store.session.get('config').adaptive) setNextStimulus();
        return mediaAssets.audio[camelize(store.session('currentStimulus').quest)];
    default:
        if (store.session.get('config').adaptive) setNextStimulus();
        return mediaAssets.audio[camelize(store.session('currentStimulus').stimulus)];
}
  • on_finish, don't forget to add the responses.
on_finish: () => {
  if (store.session.get('config').adaptive) store.session.set('previousAnswer', store.session('response'));
  if (store.session.get('config').adaptive) store.session.set('previousItem', store.session('currentStimulus'));
},

6. Fetch and Parse Corpus for Clowder (config/corpus.js)

  • Add the needed rows to the corpus handler
const csvAssets = {
    test: store.session.get('config')?.adaptive
      ? corpusTranslations[i18next.language].testCat
      : corpusTranslations[i18next.language].test,
    practice: store.session.get('config')?.adaptive
      ? corpusTranslations[i18next.language].practiceCat
      : corpusTranslations[i18next.language].practice,
  };
// Add CAT corpus-specific columns if in CAT mode
const transformCSV = (csvInput) => {
    if (store.session.get('config')?.adaptive) {
        ['practiceFSM', 'practiceLSM', 'practiceDEL', 'fsm', 'lsm', 'del', 'total'].forEach((op) => {
          ['a', 'b', 'c', 'd'].forEach((suffix) => {
            const key = `${op}.${suffix}`;
            newRow[key] = row[key]; // Assign the value from csvInput
          });
        });
      }
    accum.push(newRow);
    return accum;
  }, []);

7. Adding new corpus(experiment/i18n.js)

  • Add the new corpus files
import enCorpusPracticeCat from './config/corpus/en/practice-cat.csv';
import enCorpusTestCat from './config/corpus/en/test-cat.csv';

en: {
    test: enCorpusTest,
    practice: enCorpusPractice,
    practiceCat: enCorpusPracticeCat, //cat corpus
    testCat: enCorpusTestCat, //cat corpus
  },

Edit this page
Last Updated:
Contributors: emily-ejag
Prev
Multichoice - Clowder
Next
ARF & CALF - Clowder