StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics
https://doi.org/10.1109/TVCG.2020.3030352
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
557 lines
13 KiB
557 lines
13 KiB
'use strict';
|
|
|
|
var use = require('use');
|
|
var util = require('util');
|
|
var debug = require('debug')('questions');
|
|
var Options = require('option-cache');
|
|
var Question = require('./lib/question');
|
|
var utils = require('./lib/utils');
|
|
|
|
/**
|
|
* Answer cache, for caching answers during a session,
|
|
* and potentially across instances
|
|
*/
|
|
|
|
var sessionAnswers = {};
|
|
|
|
/**
|
|
* Create an instance of `Questions` with the given `options`.
|
|
*
|
|
* ```js
|
|
* var Questions = new Questions(options);
|
|
* ```
|
|
* @param {Object} `options` question cache options
|
|
* @api public
|
|
*/
|
|
|
|
function Questions(options) {
|
|
if (!(this instanceof Questions)) {
|
|
return new Questions(options);
|
|
}
|
|
|
|
Options.call(this, utils.omitEmpty(options || {}));
|
|
use(this);
|
|
this.initQuestions(this.options);
|
|
}
|
|
|
|
/**
|
|
* Inherit `options-cache`
|
|
*/
|
|
|
|
util.inherits(Questions, Options);
|
|
|
|
/**
|
|
* Intialize question-cache
|
|
*/
|
|
|
|
Questions.prototype.initQuestions = function(opts) {
|
|
debug('initializing question-cache');
|
|
this.answers = sessionAnswers;
|
|
this.inquirer = opts.inquirer || utils.inquirer();
|
|
this.project = opts.project || utils.project(process.cwd());
|
|
this.Question = opts.Question || Question;
|
|
this.data = opts.data || {};
|
|
this.cache = {};
|
|
this.queue = [];
|
|
};
|
|
|
|
/**
|
|
* Calls [addQuestion](#addQuestion), with the only difference being that `.set`
|
|
* returns the `questions` instance and `.addQuestion` returns the question object.
|
|
* So use `.set` if you want to chain questions, or `.addQuestion` if you need
|
|
* the created question object.
|
|
*
|
|
* ```js
|
|
* questions
|
|
* .set('drink', 'What is your favorite beverage?')
|
|
* .set('color', 'What is your favorite color?')
|
|
* .set('season', 'What is your favorite season?');
|
|
*
|
|
* // or
|
|
* questions.set('drink', {
|
|
* type: 'input',
|
|
* message: 'What is your favorite beverage?'
|
|
* });
|
|
*
|
|
* // or
|
|
* questions.set({
|
|
* name: 'drink'
|
|
* type: 'input',
|
|
* message: 'What is your favorite beverage?'
|
|
* });
|
|
* ```
|
|
* @param {Object|String} `name` Question name, message (string), or question/options object.
|
|
* @param {Object|String} `value` Question message (string), or question/options object.
|
|
* @param {Object|String} `options` Question/options object.
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.set = function(name, val, options) {
|
|
this.addQuestion.apply(this, arguments);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Add a question to be asked at a later point. Creates an instance of
|
|
* [Question](#question), so any `Question` options or settings may be used.
|
|
* Also, the default `type` is `input` if not defined by the user.
|
|
*
|
|
* ```js
|
|
* questions.addQuestion('drink', 'What is your favorite beverage?');
|
|
*
|
|
* // or
|
|
* questions.addQuestion('drink', {
|
|
* type: 'input',
|
|
* message: 'What is your favorite beverage?'
|
|
* });
|
|
*
|
|
* // or
|
|
* questions.addQuestion({
|
|
* name: 'drink'
|
|
* type: 'input',
|
|
* message: 'What is your favorite beverage?'
|
|
* });
|
|
* ```
|
|
* @param {Object|String} `name` Question name, message (string), or question/options object.
|
|
* @param {Object|String} `value` Question message (string), or question/options object.
|
|
* @param {Object|String} `options` Question/options object.
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.addQuestion = function(name, val, options) {
|
|
if (utils.isObject(name) && !utils.isQuestion(name)) {
|
|
return this.visit('set', name);
|
|
}
|
|
|
|
options = utils.merge({}, this.options, options);
|
|
var question = new this.Question(name, val, options);
|
|
debug('questions#set "%s"', name);
|
|
|
|
this.emit('set', question.name, question);
|
|
this.cache[question.name] = question;
|
|
|
|
utils.union(this.queue, [question.name]);
|
|
this.run(question);
|
|
return question;
|
|
};
|
|
|
|
/**
|
|
* Create a "choices" question from an array of values.
|
|
*
|
|
* ```js
|
|
* questions.choices('foo', ['a', 'b', 'c']);
|
|
*
|
|
* // or
|
|
* questions.choices('foo', {
|
|
* message: 'Favorite letter?',
|
|
* choices: ['a', 'b', 'c']
|
|
* });
|
|
* ```
|
|
* @param {String} `key` Question key
|
|
* @param {String} `msg` Question message
|
|
* @param {Array} `items` Choice items
|
|
* @param {Object|Function} `options` Question options or callback function
|
|
* @param {Function} `callback` callback function
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.choices = function(key, msg, items, options) {
|
|
var choices = utils.toChoices();
|
|
var question = choices.apply(null, arguments);
|
|
if (!question.hasOwnProperty('save')) {
|
|
question.save = false;
|
|
}
|
|
this.set(question.name, question);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Create a "list" question from an array of values.
|
|
*
|
|
* ```js
|
|
* questions.list('foo', ['a', 'b', 'c']);
|
|
*
|
|
* // or
|
|
* questions.list('foo', {
|
|
* message: 'Favorite letter?',
|
|
* choices: ['a', 'b', 'c']
|
|
* });
|
|
* ```
|
|
* @param {String} `key` Question key
|
|
* @param {String} `msg` Question message
|
|
* @param {Array} `list` List items
|
|
* @param {String|Array} `queue` Name or array of question names.
|
|
* @param {Object|Function} `options` Question options or callback function
|
|
* @param {Function} `callback` callback function
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.list = function(key, msg, list, options) {
|
|
var choices = utils.toChoices({type: 'list'});
|
|
var question = choices.apply(null, arguments);
|
|
if (options) {
|
|
question.options = options;
|
|
}
|
|
this.set(question.name, question);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Create a "rawlist" question from an array of values.
|
|
*
|
|
* ```js
|
|
* questions.rawlist('foo', ['a', 'b', 'c']);
|
|
*
|
|
* // or
|
|
* questions.rawlist('foo', {
|
|
* message: 'Favorite letter?',
|
|
* choices: ['a', 'b', 'c']
|
|
* });
|
|
* ```
|
|
* @param {String} `key` Question key
|
|
* @param {String} `msg` Question message
|
|
* @param {Array} `list` List items
|
|
* @param {String|Array} `queue` Name or array of question names.
|
|
* @param {Object|Function} `options` Question options or callback function
|
|
* @param {Function} `callback` callback function
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.rawlist = function(key, msg, list, options) {
|
|
var choices = utils.toChoices({type: 'rawlist'});
|
|
var question = choices.apply(null, arguments);
|
|
if (options) {
|
|
question.options = options;
|
|
}
|
|
this.set(question.name, question);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Create an "expand" question from an array of values.
|
|
*
|
|
* ```js
|
|
* questions.expand('foo', ['a', 'b', 'c']);
|
|
*
|
|
* // or
|
|
* questions.expand('foo', {
|
|
* message: 'Favorite letter?',
|
|
* choices: ['a', 'b', 'c']
|
|
* });
|
|
* ```
|
|
* @param {String} `key` Question key
|
|
* @param {String} `msg` Question message
|
|
* @param {Array} `list` List items
|
|
* @param {String|Array} `queue` Name or array of question names.
|
|
* @param {Object|Function} `options` Question options or callback function
|
|
* @param {Function} `callback` callback function
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.expand = function(name, msg, list, options) {
|
|
var choices = utils.toChoices({type: 'expand'});
|
|
var question = choices.apply(null, arguments);
|
|
if (options) {
|
|
question.options = options;
|
|
}
|
|
this.set(question.name, question);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Create a "choices" question from an array of values.
|
|
*
|
|
* ```js
|
|
* questions.choices('foo', ['a', 'b', 'c']);
|
|
* // or
|
|
* questions.choices('foo', {
|
|
* message: 'Favorite letter?',
|
|
* choices: ['a', 'b', 'c']
|
|
* });
|
|
* ```
|
|
* @param {String|Array} `queue` Name or array of question names.
|
|
* @param {Object|Function} `options` Question options or callback function
|
|
* @param {Function} `callback` callback function
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.confirm = function() {
|
|
var question = this.addQuestion.apply(this, arguments);
|
|
question.type = 'confirm';
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Get question `name`, or group `name` if question is not found.
|
|
* You can also do a direct lookup using `quesions.cache['foo']`.
|
|
*
|
|
* ```js
|
|
* var name = questions.get('name');
|
|
* //=> question object
|
|
* ```
|
|
* @param {String} `name`
|
|
* @return {Object} Returns the question object.
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.get = function(key) {
|
|
return !utils.isQuestion(key) ? this.cache[key] : key;
|
|
};
|
|
|
|
/**
|
|
* Returns true if `questions.cache` or `questions.groups` has
|
|
* question `name`.
|
|
*
|
|
* ```js
|
|
* var name = questions.has('name');
|
|
* //=> true
|
|
* ```
|
|
* @return {String} The name of the question to check
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.has = function(key) {
|
|
for (var prop in this.cache) {
|
|
if (prop.indexOf(key) === 0) return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Delete the given question or any questions that have the given
|
|
* namespace using dot-notation.
|
|
*
|
|
* ```js
|
|
* questions.del('name');
|
|
* questions.get('name');
|
|
* //=> undefined
|
|
*
|
|
* // using dot-notation
|
|
* questions.del('author');
|
|
* questions.get('author.name');
|
|
* //=> undefined
|
|
* ```
|
|
* @return {String} The name of the question to delete
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.del = function(key) {
|
|
for (var prop in this.cache) {
|
|
if (prop.indexOf(key) === 0) {
|
|
delete this.cache[prop];
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Clear all cached answers.
|
|
*
|
|
* ```js
|
|
* questions.clearAnswers();
|
|
* ```
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.clearAnswers = function() {
|
|
this.answers = sessionAnswers = {};
|
|
this.data = {};
|
|
};
|
|
|
|
/**
|
|
* Clear all questions from the cache.
|
|
*
|
|
* ```js
|
|
* questions.clearQuestions();
|
|
* ```
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.clearQuestions = function() {
|
|
this.cache = {};
|
|
this.queue = [];
|
|
};
|
|
|
|
/**
|
|
* Clear all cached questions and answers.
|
|
*
|
|
* ```js
|
|
* questions.clear();
|
|
* ```
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.clear = function() {
|
|
this.clearQuestions();
|
|
this.clearAnswers();
|
|
};
|
|
|
|
/**
|
|
* Ask one or more questions, with the given `options` and callback.
|
|
*
|
|
* ```js
|
|
* questions.ask(['name', 'description'], function(err, answers) {
|
|
* console.log(answers);
|
|
* });
|
|
* ```
|
|
* @param {String|Array} `queue` Name or array of question names.
|
|
* @param {Object|Function} `options` Question options or callback function
|
|
* @param {Function} `callback` callback function
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.ask = function(queue, config, cb) {
|
|
if (typeof queue === 'function') {
|
|
return this.ask.call(this, this.queue, {}, queue);
|
|
}
|
|
if (typeof config === 'function') {
|
|
return this.ask.call(this, queue, {}, config);
|
|
}
|
|
|
|
var questions = this.buildQueue(queue);
|
|
var answers = this.answers;
|
|
var self = this;
|
|
|
|
utils.eachSeries(questions, function(key, next) {
|
|
debug('asking question "%s"', key);
|
|
|
|
try {
|
|
var opts = utils.merge({}, self.options, config);
|
|
var data = utils.merge({}, self.data, opts);
|
|
|
|
var question = self.get(key);
|
|
var options = question._options = question.opts(opts);
|
|
var val = question.getAnswer(answers, data, self);
|
|
|
|
// emit question before building options
|
|
self.emit('ask', val, key, question, answers);
|
|
|
|
// get val again after emitting `ask`
|
|
val = question.getAnswer(answers, data, self);
|
|
debug('using answer %j', val);
|
|
|
|
// re-build options object after emitting ask, to allow
|
|
// user to update question options from a listener
|
|
options = question._options = question.opts(opts, question.options);
|
|
debug('using options %j', options);
|
|
|
|
if (options.enabled('skip')) {
|
|
debug('skipping question "%s", using answer "%j"', key, val);
|
|
self.emit('answer', val, key, question, answers);
|
|
question.next(val, self, answers, next);
|
|
return;
|
|
}
|
|
|
|
var force = options.get('force');
|
|
var isForced = force === true || utils.matchesKey(force, key);
|
|
if (!isForced && utils.isAnswer(val)) {
|
|
debug('question "%s", using answer "%j"', key, val);
|
|
utils.set(answers, key, val);
|
|
self.emit('answer', val, key, question, answers);
|
|
question.next(val, self, answers, next);
|
|
return;
|
|
}
|
|
|
|
self.inquirer.prompt([question], function(answer) {
|
|
debug('answered "%s" with "%j"', key, answer);
|
|
|
|
try {
|
|
var val = answer[key];
|
|
if (question.type === 'checkbox') {
|
|
val = utils.flatten(val);
|
|
}
|
|
|
|
if (!utils.isAnswer(val)) {
|
|
next(null, answers);
|
|
return;
|
|
}
|
|
|
|
// set answer on 'answers' cache
|
|
utils.set(answers, key, val);
|
|
|
|
// emit answer
|
|
self.emit('answer', val, key, question, answers);
|
|
|
|
// next question
|
|
question.next(val, self, answers, next);
|
|
} catch (err) {
|
|
self.emit('error', err);
|
|
next(err);
|
|
}
|
|
});
|
|
|
|
} catch (err) {
|
|
self.emit('error', err);
|
|
next(err);
|
|
}
|
|
}, function(err) {
|
|
if (err) return cb(err);
|
|
self.emit('answers', answers);
|
|
cb(null, answers);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Build an array of names of questions to ask.
|
|
*
|
|
* @param {Array|String} keys
|
|
* @return {Object}
|
|
*/
|
|
|
|
Questions.prototype.buildQueue = function(questions) {
|
|
questions = utils.arrayify(questions);
|
|
var len = questions.length;
|
|
var queue = [];
|
|
var idx = -1;
|
|
|
|
if (len === 0) {
|
|
queue = this.queue;
|
|
}
|
|
|
|
while (++idx < len) {
|
|
utils.union(queue, this.normalize(questions[idx]));
|
|
}
|
|
return queue;
|
|
};
|
|
|
|
/**
|
|
* Normalize the given value to return an array of question keys.
|
|
*
|
|
* @param {[type]} key
|
|
* @return {[type]}
|
|
* @api public
|
|
*/
|
|
|
|
Questions.prototype.normalize = function(name) {
|
|
debug('normalizing %j', name);
|
|
|
|
// get `name` from question object
|
|
if (utils.isQuestion(name)) {
|
|
return [name.name];
|
|
}
|
|
|
|
if (this.cache.hasOwnProperty(name)) {
|
|
return [name];
|
|
}
|
|
|
|
// filter keys with dot-notation
|
|
var matched = 0;
|
|
var keys = [];
|
|
for (var prop in this.cache) {
|
|
if (this.cache.hasOwnProperty(prop)) {
|
|
if (prop.indexOf(name) === 0) {
|
|
keys.push(prop);
|
|
matched++;
|
|
}
|
|
}
|
|
}
|
|
return keys;
|
|
};
|
|
|
|
/**
|
|
* Expose `Questions`
|
|
*/
|
|
|
|
module.exports = Questions;
|
|
|