haystacks-tt
Version:
A simple Haystacks-async based command line typing tutor program for Windows, Mac & Linux.
876 lines (848 loc) • 134 kB
JavaScript
/* eslint-disable no-undef */
/**
* @file accountBroker.js
* @module accountBroker
* @description Contains all code for managing accounts, and account data.
* @requires module:application.configuration.constants
* @requires module:application.constants
* @requires module:application.message.constants
* @requires module:application.system.constants
* @requires {@link https://www.npmjs.com/package/@haystacks/async|@haystacks/async}
* @requires {@link https://www.npmjs.com/package/@haystacks/constants|@haystacks/constants}
* @requires {@link https://www.npmjs.com/package/chalk|chalk}
* @requires {@link https://www.npmjs.com/package/speaker|speaker}
* @requires {@link https://www.npmjs.com/package/pcm-util|pcm-util}
* @requires {@link https://www.npmjs.com/package/path|path}
* @author Seth Hollingsead
* @date 2023/02/28
* @copyright Copyright © 2023-… by Seth Hollingsead. All rights reserved
*/
// Internal imports
import * as app_cfg from '../constants/application.configuration.constants.js';
import * as apc from '../constants/application.constants.js';
import * as app_msg from '../constants/application.message.constants.js';
import * as app_sys from '../constants/application.system.constants.js';
// External imports
import haystacks from '@haystacks/async';
import hayConst from '@haystacks/constants';
// import Speaker from 'speaker';
// import pcmUtils from 'pcm-util';
import chalk from 'chalk';
import path from 'path';
// const { createPCMData } = pcmUtils;
const {bas, biz, clr, cfg, gen, msg, num, phn, unt, wrd} = hayConst;
const baseFileName = path.basename(import.meta.url, path.extname(import.meta.url));
// application.haystacks-tt.brokers.accountBroker.
const namespacePrefix = wrd.capplication + bas.cDot + apc.cApplicationName + bas.cDot + wrd.cbrokers + bas.cDot + baseFileName + bas.cDot;
// Initialize the player so we have access to the system speaker. Generate a tone when the user types an incorrect key.
// This is part of an important learning strategy part of reinforcement learning through punishment, known as Operant conditioning.
/**
* @function getAccountData
* @description Recovers the currently loaded account data from its storage location on the Haystacks D-data structure data storage hive.
* @return {object} A JSON object that contains all of the currently loaded account data.
* @author Seth Hollingsead
* @date 2023/02/28
*/
async function getAccountData() {
let functionName = getAccountData.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
let returnData = false;
// Get the account data that was loaded on startup.
let rawAccountData = await haystacks.getData(app_sys.cuserAccounts);
// rawAccountData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.crawAccountDataIs + JSON.stringify(rawAccountData));
// For some reason the JSON data loads the data into a debugSettings data object,
// I suspect this has something to do with how JSON data is loaded by Haystacks for the debug configuration settings.
// Anyway, we can just go with it, it doesn't hurt.
returnData = rawAccountData[cfg.cdebugSettings];
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function getUserAccountData
* @description Recovers data for a specific user, if the username is found.
* @param {string} accountName The name of the user for which data should be recovered.
* @return {object|boolean} A JSON object that contains all of a users data, or False if no user matches the input name.
* @author Seth Hollingsead
* @date 2023/02/28
*/
async function getUserAccountData(accountName) {
let functionName = getUserAccountData.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// accountName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.caccountNameIs + accountName);
let returnData = false;
if (await doesAccountExist(accountName) === true) {
let userAccountData = await getAccountData();
// userAccountData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserAccountDataIs + JSON.stringify(userAccountData));
for (let userAccountKey in userAccountData) {
// userAccountKey is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserAccountKeyIs + userAccountKey);
if (userAccountKey === accountName){
returnData = userAccountData[userAccountKey];
break;
}
} // End-for (let userAccountKey in userAccountData)
} // End-if (await doesAccountExist(accountName) === true)
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function storeAccountData
* @description Stores account data to the Haystacks D-data structure data storage hive.
* @return {boolean} True or False to indicate if the storage was completed successfully or not.
* @author Seth Hollingsead
* @date 2023/02/28
*/
async function storeAccountData(dataToStore) {
let functionName = storeAccountData.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// dataToStore is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cdataToStoreIs + JSON.stringify(dataToStore));
let returnData = false;
returnData = await haystacks.storeData(app_sys.cuserAccounts, {[cfg.cdebugSettings]: dataToStore});
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function doesUserHaveCurriculumIndex
* @param {integer} curriculumIndex The index of the curriculum that we should verify exists in the users data lesson records.
* @return {boolean} True or False to indicate if the curriculumIndex exists in the users lesson data records.
* @author Seth Hollingsead
* @date 2024/09/04
*/
async function doesUserHaveCurriculumIndex(curriculumIndex) {
let functionName = doesUserHaveCurriculumIndex.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// curriculumIndex is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.ccurriculumIndexIs + curriculumIndex);
let returnData = false;
let currentUserAccountName = await currentUserAccount();
// currentUserAccountName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.ccurrentUserAccountNameIs + currentUserAccountName);
let allAccountsData = await getAccountData();
// allAccountsData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.callAccountsDataIs + JSON.stringify(allAccountsData));
for (let userName in allAccountsData) {
let userData = allAccountsData[userName];
// userName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserNameIs + JSON.stringify(userName));
// userData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserDataIs + JSON.stringify(userData));
if (userName === currentUserAccountName) {
// We found the matching user account.
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cWeFoundMatchingUserAccount);
for (let curriculumData of userData) {
// curriculumData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.ccurriculumDataIs + JSON.stringify(curriculumData));
if (curriculumData.curriculumIndex === curriculumIndex) {
// We found the matching curriculumIndex.
returnData = true;
}
} // End-for (let curriculumData of userData)
} // End-if (userName === currentUserAccountName)
} // End-for (let userName in allAccountsData)
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function appendUsersLessonScoreData
* @description Adds a users lesson score data to a users account data according to the lesson number.
* @param {object} dataToAppend A JSON object that contains lesson scores data.
* @param {integer} lessonNumber The lesson number for which the data should apply.
* @param {integer} optionalCurriculumIndex The index of the curriculum where the lesson should be added.
* @author Seth Hollingsead
* @date 2023/03/06
*/
async function appendUsersLessonScoreData(dataToAppend, lessonNumber, optionalCurriculumIndex) {
let functionName = appendUsersLessonScoreData.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// dataToAppend is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cdataToAppendIs + JSON.stringify(dataToAppend));
// lessonNumber is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNumberIs + lessonNumber);
// optionalCurriculumIndex is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.coptionalCurriculumIndexIs + optionalCurriculumIndex);
let returnData = false;
// We should probably find out if the user has any data for the optionalCurriculumIndex.
// It may be the case that the user has just finished their very first lesson in a new curriculum
// and there is no object for the optionalCurriculumIndex.
let userHasCurriculumIndex = await doesUserHaveCurriculumIndex(optionalCurriculumIndex);
// userHasCurriculumIndex is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserHasCurriculumIndexIs + userHasCurriculumIndex);
let currentUserAccountName = await currentUserAccount();
// currentUserAccountName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.ccurrentUserAccountNameIs + currentUserAccountName);
let lessonName = await getIndividualLessonName(lessonNumber, optionalCurriculumIndex);
// lessonName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNameIs + lessonName);
let allAccountsData = await getAccountData();
// allAccountsData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.callAccountsDataIs + JSON.stringify(allAccountsData));
if (!userHasCurriculumIndex) {
// We need to add a curriculum object to the users account data,
// before we get the users account data and start iterating over it to add the lesson record to it.
for (let userName1 in allAccountsData) {
// userName2 is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserName1Is + JSON.stringify(userName1));
if (userName1 === currentUserAccountName) {
// We found the matching user account, userName1
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cWeFoundMatchingUserAccount + app_msg.cuserName + num.c1);
let userData1 = allAccountsData[userName1];
// userData1 is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserData1Is + JSON.stringify(userData1));
// Create the new curriculum object
let newCurriculum = {
[app_sys.ccurriculumName]: await getCurriculumNameFromIndex(optionalCurriculumIndex),
[app_sys.ccurriculumIndex]: optionalCurriculumIndex,
[wrd.cLessons]: []
};
// Add new curriculum object to user data
userData1.push(newCurriculum);
break;
} // End-if (userName1 === currentUserAccountName)
} // End-for (let userName1 in allAccountsData)
} // End-if (!userHasCurriculumIndex)
for (let userName2 in allAccountsData) {
let userData2 = allAccountsData[userName2];
// userName2 is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserName2Is + JSON.stringify(userName2));
// userData2 is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserData2Is + JSON.stringify(userData2));
if (userName2 === currentUserAccountName) {
// We found the matching user account. userName2
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cWeFoundMatchingUserAccount + app_msg.cuserName + num.c2);
for (let curriculumData of userData2) {
// curriculumData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.ccurriculumDataIs + JSON.stringify(curriculumData));
if (curriculumData.curriculumIndex === optionalCurriculumIndex) {
// We found the matching curriculumIndex.
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cWeFoundMatchingCurriculumIndex);
let lessons = curriculumData.Lessons;
let lessonFound = false;
// lessons is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonsIs + JSON.stringify(lessons));
for (let lesson of lessons) {
let existingLessonName = Object.keys(lesson)[0];
// existingLessonName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cexistingLessonNameIs + existingLessonName);
if (existingLessonName === lessonName) {
// We found the matching lessonName.
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cWeFoundMatchingLessonName);
lesson[existingLessonName].push(dataToAppend);
lessonFound = true;
break;
}
} // End-for (let lesson of lessons)
if (!lessonFound) {
let newLesson = {};
newLesson[lessonName] = [dataToAppend];
lessons.push(newLesson);
}
break;
} // End-if (curriculumData.curriculumIndex === optionalCurriculumIndex)
} // End-for (let curriculumData of userData2)
break;
} // End-if (userName2 === currentUserAccountName)
} // End-for (let userAccountKey in allAccountsData)
returnData = allAccountsData;
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function getUsersLessonScoreData
* @description Finds the lesson score data for the current user and returns the entire collection of data for the specified lesson.
* @param {integer} lessonNumber The lesson number for which the data should be returned.
* @return {object} The JSON object that contains all of the lesson data for the specified lesson number.
* @author Seth Hollingsead
* @date 2023/03/08
*/
async function getUsersLessonScoreData(lessonNumber) {
let functionName = getUsersLessonScoreData.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// lessonNumber is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNumberIs + lessonNumber);
let returnData = false;
let foundLessonData = false;
let currentUserAccountName = await currentUserAccount();
// currentUserAccountName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.currentUserAccountNameIs + currentUserAccountName);
let lessonName = await getIndividualLessonName(lessonNumber);
// lessonName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNameIs + lessonName);
let allAccountsData = await getAccountData();
// allAccountsData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.callAccountsDataIs + JSON.stringify(allAccountsData));
for (let userAccountKey in allAccountsData) {
// userAccountKey is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserAccountKeyIs + userAccountKey);
if (userAccountKey === currentUserAccountName) {
let userAccountData = allAccountsData[userAccountKey];
for (const lessonNameKey in userAccountData) {
// lessonNameKey is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNameKeyIs + lessonNameKey);
let usersLessonDataObject = userAccountData[lessonNameKey];
// usersLessonDataObject is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cusersLessonDataObjectIs + JSON.stringify(usersLessonDataObject));
let usersLessonDataObjectKeys = Object.keys(usersLessonDataObject);
// usersLessonDataObjectKeys is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cusersLessonDataObjectKeysIs + JSON.stringify(usersLessonDataObjectKeys));
if (usersLessonDataObjectKeys[0] === lessonName) {
// lessonNameKey === lessonName
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNameKeyEqualsLessonName);
let usersLessonData = userAccountData[lessonNameKey];
// usersLessonData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cusersLessonDataIs + JSON.stringify(usersLessonData));
returnData = usersLessonData;
foundLessonData = true;
break;
} // End-if (usersLessonDataObjectKeys[0] === lessonName)
} // End-for (const lessonNameKey in userAccountData)
} // End-if (userAccountKey === currentUserAccountName)
if (foundLessonData === true) {
break;
}
} // End-for (let userAccountKey in allAccountsData)
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function saveAccountData
* @description Iterates over all of the user accounts in the system and saves each of them out to JSON files under the accounts resource folder.
* @return {boolean} True or False to indicate if all of the save operations were successful or not.
* @author Seth Hollingsead
* @date 2023/03/07
*/
async function saveAccountData() {
let functionName = saveAccountData.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
let returnData = false;
let allSuccess = true;
let pathSeparator = '';
let appAccountsPath = await haystacks.getConfigurationSetting(wrd.csystem, app_cfg.cappAccountsPath);
// appAccountsPath is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cappAccountsPathIs + appAccountsPath);
// eslint-disable-next-line no-undef
if (process.platform === gen.cwin32) {
pathSeparator = bas.cBackSlash;
} else {
pathSeparator = bas.cForwardSlash;
}
let allAccountsData = await getAccountData();
// allAccountsData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.callAccountsDataIs + JSON.stringify(allAccountsData));
for (let userAccountKey in allAccountsData) {
// userAccountKey is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserAccountKeyIs + userAccountKey);
let userAccountData = allAccountsData[userAccountKey];
// userAccountData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserAccountDataIs + JSON.stringify(userAccountData));
let userAccountFilenameAndPath = appAccountsPath + pathSeparator + userAccountKey + gen.cDotJson;
let success = await haystacks.executeBusinessRules([userAccountFilenameAndPath, userAccountData], [biz.cwriteJsonData]);
if (success === false) {
// ERROR: Failure to write out the file:
console.log(app_msg.csaveAccountDataFailureMessage01 + userAccountFilenameAndPath);
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.csaveAccountDataFailureMessage01 + userAccountFilenameAndPath);
allSuccess = false;
}
} // End-for (let userAccountKey in allAccountsData)
if (allSuccess === true) {
returnData = true;
}
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function getLessonData
* @description Recovers the currently loaded lesson data from its storage location on the Haystacks D-data structure data storage hive.
* @return {object} A JSON object that contains all of the currently loaded lesson data.
* @author Seth Hollingsead
* @date 2023/02/28
*/
async function getLessonData() {
let functionName = getLessonData.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
let returnData = false;
// Get the lesson data that was loaded on startup.
let rawLessonsData = await haystacks.getData(app_sys.capplicationLessons);
// rawLessonsData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.crawLessonsDataIs + JSON.stringify(rawLessonsData));
// For some reason the JSON data loads the data into a debugSettings data object,
// I suspect this has something to do with how JSON data is loaded by Haystacks for the debug configuration settings.
// Anyway, we can just go with it, it doesn't hurt.
returnData = rawLessonsData[cfg.cdebugSettings];
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function getLessonCount
* @description Returns the number of lessons in the curriculum.
* @param optionalCurriculumIndex An optional index parameter to specify the curriculum for which the lesson count should be returned.
* If no curriculum index value is provided, or the value is negative,
* then the current curriculum index from the configuration settings will be used.
* @return {integer} The number of lessons in the current curriculum, or specified curriculum.
* @author Seth Hollingsead
* @date 2023/03/08
*/
async function getLessonCount(optionalCurriculumIndex) {
let functionName = getLessonCount.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// optionalCurriculumIndex is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.coptionalCurriculumIndexIs + optionalCurriculumIndex);
let returnData = false;
returnData = await getHighestLessonCount(optionalCurriculumIndex);
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function getIndividualLessonData
* @description Recovers the data for a specific lesson, based on an input lesson number.
* @param {integer} lessonNumber The number of the lesson for which we should get data.
* @param {integer} optionalCurriculumIndex An optional parameter for the specified curriculum index that should be used when looking up the lesson data.
* @return {object} A JSON object that contains lesson data for a specific lesson number.
* @author Seth Hollingsead
* @date 2023/03/01
*/
async function getIndividualLessonData(lessonNumber, optionalCurriculumIndex) {
let functionName = getIndividualLessonData.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// lessonNumber is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNumberIs + lessonNumber);
// optionalCurriculumIndex is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.coptionalCurriculumIndexIs + optionalCurriculumIndex);
let returnData = false;
let currentCurriculumIndex = 0;
let lessonPlanKeys = [];
if (optionalCurriculumIndex !== undefined && optionalCurriculumIndex >= 0) {
currentCurriculumIndex = optionalCurriculumIndex;
} else {
currentCurriculumIndex = await getCurrentCurriculumIndex();
}
let currentCurriculumData = await getCurriculumObject(currentCurriculumIndex);
// currentCurriculumData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.ccurrentCurriculumDataIs + JSON.stringify(currentCurriculumData));
// Make sure we are indexing the correct lesson curriculum before we try and get the individual lesson data.
lessonPlanKeys = await getLessonPlanKeysForCurriculumIndex(currentCurriculumIndex);
// lessonPlanKeys is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonPlanKeysIs + JSON.stringify(lessonPlanKeys));
if (Array.isArray(lessonPlanKeys) && lessonPlanKeys.length > 0) {
for (let lessonKey in lessonPlanKeys) {
// lessonKey is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonKeyIs + lessonKey);
let lessonKeyValue = lessonPlanKeys[lessonKey];
// lessonKeyValue is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonKeyValueIs + lessonKeyValue);
let individualLessonData = currentCurriculumData[app_sys.cLessonPlan][0][lessonKeyValue];
// individualLessonData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cindividualLessonDataIs + JSON.stringify(individualLessonData));
let lessonName = lessonKeyValue;
// lessonName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNameIs + lessonName);
let actualLessonData = individualLessonData[wrd.cLines];
// actualLessonData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cactualLessonDataIs + JSON.stringify(actualLessonData));
let currentLessonNumber = individualLessonData[wrd.cNumber];
// currentLessonNumber is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.ccurrentLessonNumberIs + currentLessonNumber);
if ((Number.isInteger(currentLessonNumber) && currentLessonNumber === lessonNumber) || parseInt(currentLessonNumber) === lessonNumber) {
returnData = individualLessonData;
break;
} else {
// ERROR: There was an error with the lesson data, invalid lesson number:
// console.log(app_msg.cErrorGetIndividualLessonDataMessage01 + lessonKey);
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cErrorGetIndividualLessonDataMessage01 + lessonKey);
}
} // End-for (let lessonKey in lessonsData[app_sys.cLessonPlan])
} // End-if (Array.isArray(lessonsData[app_sys.cLessonPlan]) && lessonsData[app_sys.cLessonPlan].length > 0)
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function getIndividualLessonName
* @description Recovers the name of a lesson, based on the input lesson number.
* @param {integer} lessonNumber The number of the lesson for which we should get a lesson name.
* @param {integer} optionalCurriculumIndex An optional parameter for the specified curriculum index that should be used when looking up the lesson data.
* @return {string} The name of the specified lesson.
* @author Seth Hollingsead
* @date 2023/03/06
*/
async function getIndividualLessonName(lessonNumber, optionalCurriculumIndex) {
let functionName = getIndividualLessonName.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// lessonNumber is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNumberIs + lessonNumber);
// optionalCurriculumIndex is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.coptionalCurriculumIndexIs + optionalCurriculumIndex);
let returnData = false;
let currentCurriculumIndex = 0;
let lessonPlanKeys = [];
if (optionalCurriculumIndex !== undefined && optionalCurriculumIndex >= 0) {
currentCurriculumIndex = optionalCurriculumIndex;
} else {
currentCurriculumIndex = await getCurrentCurriculumIndex();
}
let currentCurriculumData = await getCurriculumObject(currentCurriculumIndex);
// Make sure we are indexing the correct lesson curriculum before we try and get the individual lesson data.
lessonPlanKeys = await getLessonPlanKeysForCurriculumIndex(currentCurriculumIndex);
// lessonPlanKeys is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonPlanKeysIs + JSON.stringify(lessonPlanKeys));
if (Array.isArray(lessonPlanKeys) && lessonPlanKeys.length > 0) {
for (let lessonKey in lessonPlanKeys) {
// lessonKey is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonKeyIs + lessonKey);
let lessonKeyValue = lessonPlanKeys[lessonKey];
// lessonKeyValue is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonKeyValueIs + lessonKeyValue);
let individualLessonData = currentCurriculumData[app_sys.cLessonPlan][0][lessonKeyValue];
// individualLessonData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cindividualLessonDataIs + JSON.stringify(individualLessonData));
let lessonName = lessonKeyValue;
// lessonName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNameIs + lessonName);
let actualLessonData = individualLessonData[wrd.cLines];
// actualLessonData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cactualLessonDataIs + JSON.stringify(actualLessonData));
let currentLessonNumber = individualLessonData[wrd.cNumber];
// currentLessonNumber is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.ccurrentLessonNumberIs + currentLessonNumber);
if ((Number.isInteger(currentLessonNumber) && currentLessonNumber === lessonNumber) || parseInt(currentLessonNumber) === lessonNumber) {
returnData = lessonName;
break;
} else {
// // ERROR: There was an error with the lesson data, invalid lesson number:
// console.log(app_msg.cErrorGetIndividualLessonDataMessage01 + lessonKey);
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cErrorGetIndividualLessonDataMessage01 + lessonKey);
}
} // End-for (let lessonKey in allLessonsData[app_sys.cLessonPlan])
} // End-if (Array.isArray(allLessonsData[app_sys.cLessonPlan]) && allLessonsData[app_sys.cLessonPlan].length > 0)
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function doesAccountExist
* @description Searches account data and determines if the account exists or does not.
* @param {string} accountName The name of the account we are checking to see if it exists or not.
* @return {boolean} True or False to indicate if the account exists or not.
* @author Seth Hollingsead
* @date 2023/02/28
*/
async function doesAccountExist(accountName) {
let functionName = doesAccountExist.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// accountName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.caccountNameIs + accountName);
let returnData = false;
// Get the account data that was loaded on startup:
let userAccountData = await getAccountData();
// userAccountData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserAccountDataIs + JSON.stringify(userAccountData));
for (let userAccountKey in userAccountData) {
// userAccountKey is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserAccountKeyIs + userAccountKey);
if (userAccountKey === accountName){
returnData = true;
break;
}
} // End-for (let userAccountKey in userAccountData)
await haystacks.consoleLog(namespacePrefix, functionName, functionName + bas.cColon + msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function createAccount
* @description Does all the work of creating an account from scratch and generating all the generic account data.
* @param {string} accountName The name of the account that should be generated.
* @return {object} The newly created user account data with empty lesson records for every lesson in the typing tutor curriculum.
* @author Seth Hollingsead
* @date 2023/02/28
*/
async function createAccount(accountName) {
let functionName = createAccount.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// accountName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.caccountNameIs + accountName);
let returnData = false;
let generatedBlankLessons = await generateBlankLessonData(0);
returnData = {[accountName]: generatedBlankLessons};
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function removeAccount
* @description Removes the specified account from the data structure.
* @param {string} accountName The name of the account to be removed.
* @param {object} allAccountsData A JSON object that contains all account data.
* @return {object} The cleaned data structure with the account name removed.
* @author Seth Hollingsead
* @date 2023/02/28
*/
async function removeAccount(accountName, allAccountsData) {
let functionName = removeAccount.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// accountName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.caccountNameIs + accountName);
// allAccountsData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.callAccountsDataIs + JSON.stringify(allAccountsData));
let returnData = false;
if (accountName && allAccountsData) {
for (let userAccountKey in allAccountsData) {
// userAccountKey is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserAccountKeyIs + userAccountKey);
if (userAccountKey === accountName){
delete allAccountsData[userAccountKey];
break;
}
} // End-for (let userAccountKey in userAccountData)
returnData = allAccountsData;
}
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function generateBlankLessonData
* @description Generates an array of blank lessons data for every lesson in the typing tutor curriculum.
* @param {string} curriculumName The name of the curriculum for which lesson data should be generated.
* @return {array} An array of empty JSON objects for every lesson in the typing tutor curriculum.
* @author Seth Hollingsead
* @date 2023/02/28
*/
async function generateBlankLessonData(curriculumName) {
let functionName = generateBlankLessonData.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// curriculumName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.ccurriculumNameIs + curriculumName);
let returnData = false;
let currentCurriculumIndex = await lookupCurriculum(curriculumName);
// Get the lesson data.
let masterLessonsData = await getCurriculumObject(currentCurriculumIndex);
// masterLessonsData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cmasterLessonsData + JSON.stringify(masterLessonsData));
returnData = [];
if (Array.isArray(masterLessonsData[app_sys.cLessonPlan]) && masterLessonsData[app_sys.cLessonPlan].length > 0) {
for (let lessonKey in masterLessonsData[app_sys.cLessonPlan]) {
// lessonKey is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonKeyIs + lessonKey);
let lessonData = masterLessonsData[app_sys.cLessonPlan][lessonKey];
// lessonData is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonDataIs + JSON.stringify(lessonData));
let lessonNameArray = Object.keys(lessonData);
// lessonNameArray is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNameArrayIs + JSON.stringify(lessonNameArray));
for (let lessonNameKey in lessonNameArray) {
// lessonNameKey is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNameKeyIs + lessonNameKey);
let lessonName = lessonNameArray[lessonNameKey];
// lessonName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clessonNameIs + lessonName);
returnData.push({[lessonName]: []});
} // End-for (let lessonName in lessonNameArray)
} // End-for (let lessonKey in lessonsData[app_sys.cLessonPlan])
} // End-if (Array.isArray(lessonsData[app_sys.cLessonPlan]) && lessonsData[app_sys.cLessonPlan].length > 0)
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function currentUserAccount
* @description Gets the currently logged in user account.
* @return {string} The name of the currently logged in user account.
* @author Seth Hollingsead
* @date 2023/02/28
*/
async function currentUserAccount() {
let functionName = currentUserAccount.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
let returnData = false;
returnData = await haystacks.getConfigurationSetting(wrd.csystem, app_cfg.cCurrentUser);
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function loginUser
* @description Does the work to login the specified username.
* @param {string} accountName The username that should be logged in.
* @return {boolean} True or False to indicate if the login was completed successfully or not.
* @author Seth Hollingsead
* @date 2023/02/28
*/
async function loginUser(accountName) {
let functionName = loginUser.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// accountName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.caccountNameIs + accountName);
let returnData = false;
returnData = await haystacks.setConfigurationSetting(wrd.csystem, app_cfg.cCurrentUser, accountName);
await haystacks.consoleLog(namespacePrefix, functionName, functionName + bas.cColon + msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function logoutUser
* @description Logs the specified user out of the system, sets the currently logged in user as an empty string.
* @param {string} accountName The name of the user that should be logged out.
* Actually we don't even need the name of the currently logged in user to logout.
* @return {boolean} True or False to indicate if the user was logged out successfully or not.
* @author Seth Hollingsead
* @date 2023/02/28
*/
async function logoutUser(accountName) {
let functionName = logoutUser.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// accountName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.caccountNameIs + accountName);
let returnData = false;
returnData = await haystacks.setConfigurationSetting(wrd.csystem, app_cfg.cCurrentUser, '');
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function setCurrentCurriculum
* @description Checks to make sure that a valid user is logged in, checks if the setting adhereToCurriculumOrderRequirement
* is set or not set. If it is not set, then the current users current curriculum is set to the desired curriculum.
* If the setting is set, then also checks if the user has passed all of the necessary prerequisite lessons and curriculums.
* If the user has passed all of the necessary prerequisite lessons and curricula, then the desired curriculum is set.
* If the necessary prerequisite lessons and curricula have not been passed then an error message is presented and the
* desired curriculum is not set.
* @param {string|integer} desiredCurriculum The name or index of the desired curriculum to be set as the current curriculum.
* @return {boolean} True or False to indicate if the current curriculum was set according to the desired curriculum.
* @author Seth Hollingsead
* @date 2024/08/27
*/
async function setCurrentCurriculum(desiredCurriculum) {
let functionName = setCurrentCurriculum.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
// desiredCurriculum is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cdesiredCurriculumIs + desiredCurriculum);
let returnData = false;
let currentUser = '';
let adhereToCurriculumOrderRequirement = false;
let fullyQualifiedCurriculumName = '';
let fullyQualifiedCurriculumIndex = 0;
let passedAllPrerequisiteRequirements = true;
if (desiredCurriculum !== undefined && desiredCurriculum !== '') {
currentUser = await haystacks.getConfigurationSetting(wrd.csystem, app_cfg.cCurrentUser);
// currentUser is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.ccurrentUserIs + currentUser);
if (currentUser !== '') {
adhereToCurriculumOrderRequirement = await haystacks.getConfigurationSetting(wrd.csystem, app_cfg.cadhereToCurriculumOrderRequirement);
// adhereToCurriculumOrderRequirement is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cadhereToCurriculumOrderRequirementIs + adhereToCurriculumOrderRequirement);
fullyQualifiedCurriculumIndex = await lookupCurriculum(desiredCurriculum);
// fullyQualifiedCurriculumIndex is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cfullyQualifiedCurriculumIndexIs + fullyQualifiedCurriculumIndex);
if (fullyQualifiedCurriculumIndex !== false) {
fullyQualifiedCurriculumName = await getCurriculumNameFromIndex(fullyQualifiedCurriculumIndex);
}
// fullyQualifiedCurriculumName is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.ccfullyQualifiedCurriculumNameIs + fullyQualifiedCurriculumName);
if ((fullyQualifiedCurriculumIndex !== false && fullyQualifiedCurriculumName !== false) &&
(fullyQualifiedCurriculumIndex !== '' && fullyQualifiedCurriculumName !== '')) {
if (adhereToCurriculumOrderRequirement === true) {
let listOfCurrentCurriculumPrerequisites = await getListOfPrerequisiteCurriculumIndicesForSpecifiedIndex(fullyQualifiedCurriculumIndex);
// listOfCurrentCurriculumPrerequisites is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clistOfCurrentCurriculumPrerequisitesIs + listOfCurrentCurriculumPrerequisites);
if (listOfCurrentCurriculumPrerequisites !== '' || (Array.isArray(listOfCurrentCurriculumPrerequisites) === true && listOfCurrentCurriculumPrerequisites.length > 0)) {
// listOfCurrentCurriculumPrerequisites.length is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.clistOfCurrentCurriculumPrerequisitesLengthIs + listOfCurrentCurriculumPrerequisites.length);
if (listOfCurrentCurriculumPrerequisites.length === 1 && listOfCurrentCurriculumPrerequisites[0] !== '') {
// Determine if the user is allowed to set the current curriculum name and if all the prerequisites for the current user meet the curriculum order requirement.
// Determine if the user has completed the necessary prerequisite lessons and curriculums.
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.csetCurrentCurriculumMessage1);
for (const verifyCurriculumIndex in listOfCurrentCurriculumPrerequisites) {
// verifyCurriculumIndex is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cverifyCurriculumIndexIs + verifyCurriculumIndex);
let dataParsedVerifyCurriculumIndex = await haystacks.executeBusinessRules([verifyCurriculumIndex, ''],[biz.cstringToDataType]);
// dataParsedVerifyCurriculumIndex is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cdataParsedVerifyCurriculumIndexIs + dataParsedVerifyCurriculumIndex);
let highestLessonForCurriculum = await getHighestLessonCount(dataParsedVerifyCurriculumIndex);
let userHighestPassingLessonNumberByCurriculumIndex = await getHighestLessonNumberAboveAdvancementScoringLimit('', dataParsedVerifyCurriculumIndex);
// highestLessonForCurriculum is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.chighestLessonForCurriculumIs + highestLessonForCurriculum);
// userHighestPassingLessonNumberByCurriculumIndex is:
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cuserHighestPassingLessonNumberByCurriculumIndexIs + userHighestPassingLessonNumberByCurriculumIndex);
if (userHighestPassingLessonNumberByCurriculumIndex < highestLessonForCurriculum) {
passedAllPrerequisiteRequirements = false;
break;
}
}
if (passedAllPrerequisiteRequirements === true) {
// User has passed all of the requirements. Allowed to set the new curriculum index and name.
await haystacks.setConfigurationSetting(wrd.csystem, app_cfg.cCurrentCurriculumName, fullyQualifiedCurriculumName);
await haystacks.setConfigurationSetting(wrd.csystem, app_cfg.cCurrentCurriculumIndex, fullyQualifiedCurriculumIndex);
returnData = true;
}
} else {
await haystacks.setConfigurationSetting(wrd.csystem, app_cfg.cCurrentCurriculumName, fullyQualifiedCurriculumName);
await haystacks.setConfigurationSetting(wrd.csystem, app_cfg.cCurrentCurriculumIndex, fullyQualifiedCurriculumIndex);
returnData = true;
}
} else {
// The current curriculum name and index can be set because there are no prerequisites.
await haystacks.setConfigurationSetting(wrd.csystem, app_cfg.cCurrentCurriculumName, fullyQualifiedCurriculumName);
await haystacks.setConfigurationSetting(wrd.csystem, app_cfg.cCurrentCurriculumIndex, fullyQualifiedCurriculumIndex);
returnData = true;
}
} else {
await haystacks.setConfigurationSetting(wrd.csystem, app_cfg.cCurrentCurriculumName, fullyQualifiedCurriculumName);
await haystacks.setConfigurationSetting(wrd.csystem, app_cfg.cCurrentCurriculumIndex, fullyQualifiedCurriculumIndex);
returnData = true;
}
} else {
if (fullyQualifiedCurriculumIndex === false || fullyQualifiedCurriculumIndex === '') {
// ERROR: fullyQualifiedCurriculumIndex is not valid.
console.log(app_msg.cErrorSetCurrentCurriculumMessage3);
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cErrorSetCurrentCurriculumMessage3);
}
if (fullyQualifiedCurriculumName === false || fullyQualifiedCurriculumName === '') {
// ERROR: fullyQualifiedCurriculumName is not valid.
console.log(app_msg.cErrorSetCurrentCurriculumMessage4);
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cErrorSetCurrentCurriculumMessage4);
}
}
} else {
// ERROR: User must be logged in to set the current curriculum.
console.log(app_msg.cErrorSetCurrentCurriculumMessage1);
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cErrorSetCurrentCurriculumMessage1);
}
} else {
// ERROR: A name or index must be entered for the desired curriculum.
console.log(app_msg.cErrorSetCurrentCurriculumMessage2);
await haystacks.consoleLog(namespacePrefix, functionName, app_msg.cErrorSetCurrentCurriculumMessage2);
}
await haystacks.consoleLog(namespacePrefix, functionName, msg.creturnDataIs + JSON.stringify(returnData));
await haystacks.consoleLog(namespacePrefix, functionName, msg.cEND_Function);
return returnData;
}
/**
* @function getCurrentCurriculumName
* @description Checks to make sure the current user is logged in then gets the name of the current curriculum.
* If no user is logged in, then display an error message and return false.
* @return {string|boolean} The name of the current curriculum, or false if no user is logged in,
* or false if no current curriculum name is set.
* @author Seth Hollingsead
* @date 2024/08/27
*/
async function getCurrentCurriculumName() {
let functionName = getCurrentCurriculumName.name;
await haystacks.consoleLog(namespacePrefix, functionName, msg.cBEGIN_Function);
let returnData = false;
let currentUser = '';
currentUser = await haystacks.getConfigurationSetting(wrd.csystem, app_cfg.cCurrentUser);
if (currentUser !== '') {
returnData = await haystacks.getConfigurationSetting(w