UNPKG

@sprucelabs/spruce-cli

Version:

Command line interface for building Spruce skills.

185 lines • 7.75 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const schema_1 = require("@sprucelabs/schema"); const chalk_1 = __importDefault(require("chalk")); const lodash_1 = require("lodash"); const FormComponent_1 = __importDefault(require("./FormComponent")); var AnswerValidity; (function (AnswerValidity) { AnswerValidity["Correct"] = "correct"; AnswerValidity["Incorrect"] = "incorrect"; })(AnswerValidity || (AnswerValidity = {})); class QuizComponent { formBuilder; term; randomizeQuestions = true; originalQuestions; lastResults; constructor(options) { // We're going to build a schema from the questions and pass that to the form builder const definition = this.buildSchemaFromQuestions(options.questions); // Track questions for later reference this.originalQuestions = options.questions; // Construct new form builder this.formBuilder = new FormComponent_1.default({ ...options, schema: definition, }); // Set state locally this.term = options.ui; this.randomizeQuestions = options.randomizeQuestions ?? true; } /** Present the quiz */ async present(options = {}) { const { questions = this.formBuilder .getNamedFields() .map((nf) => nf.name), randomizeQuestions = this.randomizeQuestions, } = options; const startTime = new Date().getTime(); // Pull out answers const fields = randomizeQuestions ? (0, lodash_1.shuffle)(questions) : questions; // Ask for the answers const results = await this.formBuilder.present({ ...options, fields: fields, }); // Generate stats const answers = {}; const answerValidities = {}; const totalQuestions = questions.length; let totalCorrect = 0; const questionNames = Object.keys(results); questionNames.forEach((questionName) => { const fieldName = questionName; const answer = results[fieldName] || ''; const [validity, idx] = answer.split('-'); // Get the field to tell type const { field } = this.formBuilder .getNamedFields() .find((namedField) => namedField.name === fieldName) || {}; if (!field) { throw new Error('Field issue in QuizComponent'); } const fieldDefinition = field.definition; switch (fieldDefinition.type) { case 'select': // Pull the original multiple choice, we can cast it as multiple choice // question with confidence answers[questionName] = this.originalQuestions[questionName].answers[parseInt(idx)]; break; default: // @ts-ignore TODO proper questions to schema should fix this because we only support a few fields answers[questionName] = results[fieldName]; } // Track validity if (validity === AnswerValidity.Correct) { totalCorrect = totalCorrect + 1; answerValidities[questionName] = AnswerValidity.Correct; } else { answerValidities[questionName] = AnswerValidity.Incorrect; } }, 0); const totalWrong = totalQuestions - totalCorrect; // Track time const endTime = new Date().getTime(); this.lastResults = { percentCorrect: totalCorrect / totalQuestions, totalCorrect, totalWrong, answerValidities: answerValidities, answers: answers, totalQuestions, time: { startTimeMs: startTime, endTimeMs: endTime, totalTimeSec: +((endTime - startTime) / 1000).toFixed(1), }, }; return this.lastResults; } async scorecard(options = {}) { const { headline, results = this.lastResults } = options; const { term } = this; if (!results) { throw new schema_1.SchemaError({ code: 'INVALID_PARAMETERS', parameters: [], }); } term.clear(); term.renderHero(headline ?? 'Quiz results!'); const testResults = {}; this.formBuilder.getNamedFields().forEach((namedField) => { const { name, field } = namedField; const questionFieldName = name; // Get results const isCorrect = results.answerValidities[questionFieldName] === AnswerValidity.Correct; const guessedAnswer = `${results.answers[questionFieldName]}`; // Build the real answer let correctAnswer = ''; const originalQuestion = this.originalQuestions[questionFieldName]; switch (originalQuestion.type) { case 'select': correctAnswer = originalQuestion.answers[0]; break; default: // All options just pass through the answer tied to the question during instantiation correctAnswer = originalQuestion.answer; } const objectKey = field.label || '**missing'; if (isCorrect) { testResults[objectKey] = `${chalk_1.default.bgGreenBright.black('Correct!')} ${guessedAnswer} `; } else { testResults[objectKey] = `${chalk_1.default.bgRedBright.black('Wrong!')} ${chalk_1.default.strikethrough(guessedAnswer)} -> ${correctAnswer}`; } }); term.renderObject(testResults); term.renderLine(`# questions: ${results.totalQuestions}`); term.renderLine(`# correct: ${results.totalCorrect}`); term.renderHeadline(`Your score: ${(results.percentCorrect * 100).toFixed(1)}%`); await term.waitForEnter(); } /** Takes questions and builds a schema */ buildSchemaFromQuestions(questions) { // TODO change SchemaFields to something based on schema generated from questions const fields = {}; Object.keys(questions).forEach((fieldName) => { const question = questions[fieldName]; switch (question.type) { case 'select': fields[fieldName] = { type: question.type, label: question.question, options: { choices: (0, lodash_1.shuffle)(question.answers.map((question, idx) => ({ value: idx === 0 ? `${AnswerValidity.Correct}-${idx}` : `${AnswerValidity.Incorrect}-${idx}`, label: question, }))), }, }; break; default: fields[fieldName] = { type: question.type, label: question.question, }; } }); //@ts-ignore TODO better mapping of questions to schema definition const definition = { id: 'quizGenerated', name: 'Generated quiz', fields, }; return definition; } } exports.default = QuizComponent; //# sourceMappingURL=QuizComponent.js.map