@sprucelabs/spruce-cli
Version:
Command line interface for building Spruce skills.
185 lines • 7.75 kB
JavaScript
;
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