UNPKG

haystacks-tt

Version:

A simple Haystacks-async based command line typing tutor program for Windows, Mac & Linux.

876 lines (848 loc) 134 kB
/* 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