QScript Selection Functions
The QScript functions selectOne and selectMany allow the user of the QScript to select from a list of strings. The functions on this page are designed to make it easier for the QScript writer to present menus of Q objects, like questions, variables, or data files, for the user to select from without the need to write extra code to extract the labels or names of these objects.
To make these functions available when writing a QScript or Rule see JavaScript Reference.
selectManyQuestions(message, question_array, use_preselections)
selectManyQuestions() allows you to provide an array of Q Question objects (obtained, for example, by getQuestionsByName()) and a message string. The function obtains the names of the questions and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:
- questions - is an array of the selected questions
- names - is an array of the names of the selected questions
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectManyVariablesByName(message, variable_array, use_preselections)
selectManyVariablesByName() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the names of the variables and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:
- variables - is an array of the selected variables
- names - is an array of the names of the selected variables
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectManyVariablesByLabel(message, variable_array, use_preselections)
selectManyVariablesByLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByLabel()) and a message string. The function obtains the labels of the variables and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:
- variables - is an array of the selected variables
- labels - is an array of the labels of the selected variables
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectManyVariablesByNameAndLabel(message, variable_array, use_preselections)
selectManyVariablesByNameAndLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByLabel()) and a message string. The function obtains the names and labels of the variables and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:
- variables - is an array of the selected variables
- names_labels - is an array of the names and labels of the selected variables
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectManyVariablesByQuestionNameAndLabel(message, variable_array, use_preselections)
selectManyVariablesByQuestionNameAndLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByLabel()) and a message string. The function obtains the question names and variable labels of the variables and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:
- variables - is an array of the selected variables
- names_labels - is an array of the names and labels of the selected variables
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectManyVariablesByQuestionNameAndLabelAndDataFile(message, variable_array, use_preselections)
selectManyVariablesByQuestionNameAndLabelAndDataFile() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByLabel()) and a message string. The function obtains the question names and variable labels and data file names of the variables and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:
- variables - is an array of the selected variables
- names_labels - is an array of the names and labels of the selected variables
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectManyDataFiles(message, datafile_array)
selectManyDataFiles() allows you to provide an array of Q Data File objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the names of the data files and presents them to the user in a box with your message. The user can make multiple selections. The function returns an object with two properties:
- files - is an array of the selected files
- names - is an array of the names of the selected files
selectOneDataFileFromSelectedQuestions()
Intended for use in Displayr only, selectOneDataFileFromSelectedQuestions() either returns the only dataFile in the report, or if there are multiple dataFiles, uses project.report.selectedQuestions() to determine which dataFile to report based on which one the variable set the user has selected from Data Sets. An error is thrown if the user has selected multiple questions from different data sets or if there are multiple data sets and nothing is selected.
selectOneQuestion(message, question_array, use_preselections)
selectOneQuestion() allows you to provide an array of Q Question objects (obtained, for example, by getQuestionsByName()) and a message string. The function obtains the names of the questions and presents them to the user in a box with your message. The user can only make a single selection. The function returns the question object that the user has selected.
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectOneQuestionByNameAndDataFile(message, question_array, use_preselections)
selectOneQuestionByNameAndDataFile() allows you to provide an array of Q Question objects (obtained, for example, by getQuestionsByName()) and a message string. The function obtains the names of the questions with the data file name and presents them to the user in a box with your message. The user can only make a single selection. The function returns the question object that the user has selected.
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectOneVariableByLabel(message, variable_array, use_preselections)
selectOneVariableByLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByLabel()) and a message string. The function obtains the labels of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the variable object that the user has selected.
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectOneVariableByName(message, variable_array, use_preselections)
selectOneVariableByName() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the names of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the variable object that the user has selected.
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectOneVariableByNameAndLabel(message, variable_array, use_preselections)
selectOneVariableByNameAndLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the names and labels of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the variable object that the user has selected.
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectOneVariableByQuestionNameAndLabel(message, variable_array, use_preselections)
selectOneVariableByQuestionNameAndLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the question names and variable labels of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the variable object that the user has selected.
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectOneVariableByQuestionNameAndLabelAndDataFile(message, variable_array, use_preselections)
selectOneVariableByQuestionNameAndLabel() allows you to provide an array of Q Variable objects (obtained, for example, by getVariablesByName()) and a message string. The function obtains the question names and variable labels and data file names of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the variable object that the user has selected.
The boolean flag use_preselections is used to specify whether or not the menu shown to the user should have items already selected, based on the user's selections in the Variables and Questions tab.
selectOneDataFile(message, datafile_array, default_index)
selectOneDataFile() allows you to provide an array of Q Data File objects (obtained, for example, by project.dataFiles) and a message string. The function obtains the names of the variables and presents them to the user in a box with your message. The user can only make a single selection. The function returns the data file object that the user has selected. The parameter default_index specifies the default selection to show.
truncateStringForSelectionWindow(string)
truncateStringForSelectionWindow() truncates the input string to 120 characters. This function is used by the selection functions above to ensure that the message box does not become very wide when question names and variable labels are very long.
dataFileSelection()
This function checks the project for multiple data files, and if more than one file exists in the project it presents the user with a list of files and asks them which files they would like to use. It then returns an array containing the selected data file objects.
selectManyTablesWithGroupNames(message, group_item)
This function presents the user with a list of all of the tables in the input group_item and allows them to select multiple items.
Each item in the list contains the name of the table, along with the name of the groups that contain the table, up to but not including the name of group_item.
The function returns an object containing two properties:
- tables is an array of the selected table objects.
- names is an array containing the names of the selected tables.
selectManyTablesAndPlotsWithGroupNamesAndTypes(message, group_item)
This function presents a list of all of the tables and charts in the input group_item and allows the user to make multiple selections.
Each item in the list contains the name of the table, along with the name of the groups that contain the table, up to but not including the name of group_item, and also whether the item it a table or a chart.
The function returns an object containing two properties:
- items is an array of the selected items.
- names is an array containing the names of the selected items.
selectOneTableWithGroupNames(message, group_item)
This function presents the user with a list of all of the tables in the input group_item and allows them to select one table. Each item in the list contains the name of the table, along with the name of the groups that contain the table, up to but not including the name of group_item. The table object selected by the user is returned.
requestOneDataFileFromProject(null_if_cancelled, last_as_default)
This function requests the user to select a data file if there is more than one data file in the project, and returns the selected data file. Otherwise returns the single data file in the project. The flag null_if_cancelled controls whether to return null if the dialog is cancelled. The flag last_as_default controls whether to show the last data file as the default selection.
selectedTablesAndPlots(report)
This function returns all tables and charts selected in the report tree in Q 4.8 or later. For older versions of Q, a listbox is shown which allows the user to select from tables and charts obtained from the report tree.
recursiveGetAllTablesInGroup(group_item, table_array)
This function adds all of the tables in group_item to the array table_array. The function is called recursive because it obtains tables from the subgroups of the specified group by applying itself to each subgroup.
You should always supply an existing array variable:
var group_1_tables = []; recursiveGetAllTablesInGroup(group_1, group_1_tables);
recursiveGetAllTablesAndPlotsInGroup(group_item, table_array)
This function adds all of the tables in group_item to the array table_array. The function is called recursive because it obtains tables from the subgroups of the specified group by applying itself to each subgroup.
You should always supply an existing array variable:
var group_1_tables_plots = []; recursiveGetAllTablesAndPlotsInGroup(group_1, group_1_tables_plots);
recursiveGetAllGroupsInGroup(group_item, group_array)
This function places all pages/QScript ReportGroups in the ReportGroup, group_item, into the supplied array, group_array.
recursiveGetAllItemsInGroup(group_item, table_array)
This function places all sub-items (regardless of type) of the ReportGroup, group_item, into the supplied array, group_array. Sub-items of ReportGroup sub-items are also included recursively.
filePromptWithVerification(message, override_file_name, options)
This function is used to add a data file to the Q Project by prompting the user to enter the path and name of the file. If the user mis-types the file name then they are given the opportunity to re-enter the file name. When using Q 4.9 and above it is better to use the function addDataFileDialog() instead.
oneOrMoreQuestions(questions)
Returns true if the array questions is not empty. If it is empty it returns false and returns a message for the user. This is intended to be used in a loop to give the user additional opportunity to select questions of they failed to select a question when prompted.
indicesOfQObjectsInArray(array_1, array_2)
Returns the indices of objects in array_1 which are also elements of array_2. The two input arrays should only contain Q objects, e.g. questions, variables, etc.
selectOneVariableOrQuestionUsingPreselections(message, object_array, label_array, use_preselections, help_page)
Generalizes the function selectOne() for questions and variables, allowing any selections made by the user to be pre-selected in the list that is shown to the user. Returns the index of the item selected by the user just like selectOne().
- message: The message to show to user.
- object_array: The array of questions or variables to be selected from.
- label_array: The labels corresponding to the items in object_array, in the same order as the objects. These are shown to the user.
- use_preselections: Boolean flag to toggle whether the user is shown preselected items in the menu.
- help_page: help page to direct the user to.
selectManyVariablesOrQuestionsUsingPreselections(message, object_array, label_array, use_preselections, help_page)
Generalizes the function selectMany() for questions and variables, allowing any selections made by the user to be pre-selected in the list that is shown to the user. Returns the indices of the items selected by the user just like selectMany().
- message: The message to show to user.
- object_array: The array of questions or variables to be selected from.
- label_array: The labels corresponding to the items in object_array, in the same order as the objects. These are shown to the user.
- use_preselections: Boolean flag to toggle whether the user is shown preselected items in the menu.
- help_page: help page to direct the user to.
promptUntilBlank(message)
Repeatedly prompts the user until they enter a blank selection. Returns an array of the entered strings.
promptForDKLabels()
Prompt the user to enter extra labels corresponding to Don't Know options in the questionnaire. Returns an array containing the entered labels.
getQuestionsSelectedInTables()
Search through all of the tables and charts that have been selected by the user at the time the script is run and return an array of objects, each of which has two properties:
- question - The question object
- item - the table or chart which contains the question
- position - a string telling us if the questions is in Primary (blue), Secondary (brown), or Tertiary (the second blue menu on some charts).
replaceQuestionsInSelectedTablesAndPlots(question_pairs, replace_in_plots)
Go through each of the tables and charts that are selected at the time the QScript runs, and use the array question_pairs to replace the questions that are selected in those items.
question_pairs should be an array, where each element has two properties:
- originalQuestion - the original question that we want to replace.
- newQuestion - the new question that we want to replace originalQuestion with.
If it is not appropriate to make replacements in charts, then set replace_in_plots to false.
getSelectedROutputFromPage(required_class)
This function is designed to resolve ambiguity about which R Output the user has selected when running various Save Variables QScripts. It relaxes the restriction that the user has to specifically have the intended R Output selected, instead looking for an unambiguously appropriate output in the current page (folder). If there is a single appropriate R Output on the current page then this will be returned. When there are multiple appropriate R Outputs on the page, then a message will be returned prompting the user to select one of them.
The argument required_class is used to restrict the search to only R Outputs that have a specific R class. If you don't want to restrict to a particular class, then supply a value of null
questionsInSelectedItems()
This function returns an array of the questions that are used by all the selected tables and plots.
getAllUserSelections()
This function is designed to organize the information about which variables, questions, folders, tables, and other outputs, that the user currently has selected at the time the script is run. The point is to make it easier for the QScript author to identify what is selected without having to know the distinction between the various native selection functions, like project.report.selectedRaw(). It distiguishes between things that are directly selected, and things which are implicitly selected (for example tables may be implicitly selected by virtue of being in a subfolder of a folder that is selected directly). The returned value is a JavaScript object containing the following properties, most of which are arrays of items:
- selected_data_sets: Data Sets currently selected
- selected_questions: All questions directly selected by the user
- selected_pages: All pages directly selected by the user (includes sub pages)
- selected_tables: All tables directly selected by the user
- selected_plots: All Q plots, excluding word clouds, directly selected by the user
- selected_word_clouds: All word clouds directlt selected by the user
- selected_r_outputs: All Calculations directly selected by the user
- selected_top_level_pages: All selected pages at the top level only (that is, does not include subpages of selected pages)
- selected_variables: All variables directly selected by the user
- single_variable_questions_selected: All directly selected questions that only contain a single variable
- multiple_variable_questions_selected: All directly selected questions that contain multiple variables
- implicitly_selected_items: All items, including tables, plots, R outputs implicitly selected by the user
- implicitly_selected_tables: Tables selected implicitly by the user
- implicitly_selected_plots: Plots implicitly selected by the user
- implicitly_selected_word_clouds: Word clouds implictly selected by the user
- implicitly_selected_r_outputs: Calculations implicitly selected by the user
- questions_in_selected_tables: Questions present in the tables which are directly selected by the user
- questions_in_rows_of_selected_tables: Questions present in the rows of tables which are directly selected by the user
- questions_in_columns_of_selected_tables: Questions present in the columns of tables which are directly selected by the user
- questions_in_selected_plots: Questions present in the Q plots which are directly selected by the user
- current_page: The page the user is currently on
NOrMoreSelected(user_selections, type, n)
This function is designed to make it easy to check if the user has selected a certain number (or more) of items of a particular type. This is typically to ensure that the user has selected appropriate inputs before continuing with the rest of the QScript, or to distinguish which mode the QScript should run in (e.g. should it apply to selected tables or selected variables). This function returns true or false based on the user's selections.
The arguments are:
- user_selections - an object generated by the function getAllUserSelections() above.
- type - a string identifying the type of item that you want to ensure that the user has selected. Options are:
- "Group" - a folder in Q or a page in Displayr
- "Page" - a folder in Q or a page in Displayr
- "R Output" - an R Output (now referred to as a Calculation in Displayr)
- "Plot" - a native Q plot
- "Question" - a Question (referred to as a Variable Set in Displayr)
- "Variable Set" - a Question (referred to as a Variable Set in Displayr)
- "Variable" - a Variable
- "WordCloud" - a Word Cloud
- n - an integer specifying the minimum number of objects of the specified type which must be selected.
exactlyNSelected(user_selections, type, n)
As with NOrMoreSelected() above except that the user must have exactly the right number of the specified type of items selected.
Source Code
includeWeb("JavaScript Array Functions");
includeWeb("JavaScript Text Analysis Functions");
includeWeb("JavaScript Utilities");
includeWeb("QScript Functions for Processing Arrays");
includeWeb("QScript Utility Functions");
includeWeb("QScript Value Attributes Functions");
// SELECTION
function selectManyQuestions(message, question_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const question_names = getNamesOfQuestionsInArray(question_array);
const selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, question_array, question_names.map(truncateStringForSelectionWindow), use_preselections, null);
const selected_questions = getElementsOfArrayBySelectedIndices(question_array, selected_indices);
const selected_names = getElementsOfArrayBySelectedIndices(question_names, selected_indices);
return { questions: selected_questions, names: selected_names };
}
// Generalization of selectMany() to array of variables, allowing the user
// to make selection by variable names.
function selectManyVariablesByName(message, variable_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const variable_names = getNamesOfVariablesInArray(variable_array);
const selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, variable_array, variable_names.map(truncateStringForSelectionWindow), use_preselections, null);
const selected_variables = getElementsOfArrayBySelectedIndices(variable_array, selected_indices);
const selected_names = getElementsOfArrayBySelectedIndices(variable_names, selected_indices);
return { variables: selected_variables, names: selected_names };
}
// Generalization of selectMany() to array of variables, allowing the user
// to make selection by variable labels.
function selectManyVariablesByLabel(message, variable_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const variable_labels = getLabelsOfVariablesInArray(variable_array);
const selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, variable_array, variable_labels.map(truncateStringForSelectionWindow), use_preselections, null);
const selected_variables = getElementsOfArrayBySelectedIndices(variable_array, selected_indices);
const selected_labels = getElementsOfArrayBySelectedIndices(variable_labels, selected_indices);
return { variables: selected_variables, labels: selected_labels };
}
function selectManyVariablesByNameAndLabel(message, variable_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const variable_names_labels = getNamesAndLabelsOfVariablesInArray(variable_array);
const selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, variable_array, variable_names_labels.map(truncateStringForSelectionWindow), use_preselections, null);
const selected_variables = getElementsOfArrayBySelectedIndices(variable_array, selected_indices);
const selected_names_labels = getElementsOfArrayBySelectedIndices(variable_names_labels, selected_indices);
return { variables: selected_variables, names_labels: selected_names_labels };
}
function selectManyVariablesByQuestionNameAndLabel(message, variable_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const variable_names_labels = getQuestionNamesAndLabelsOfVariablesInArray(variable_array);
const selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, variable_array, variable_names_labels.map(truncateStringForSelectionWindow), use_preselections, null);
const selected_variables = getElementsOfArrayBySelectedIndices(variable_array, selected_indices);
const selected_names_labels = getElementsOfArrayBySelectedIndices(variable_names_labels, selected_indices);
return { variables: selected_variables, names_labels: selected_names_labels };
}
function selectManyVariablesByQuestionNameAndLabelAndDataFile(message, variable_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const variable_names_labels = getQuestionNamesAndLabelsAndDataFilesOfVariablesInArray(variable_array);
const selected_indices = selectManyVariablesOrQuestionsUsingPreselections(message, variable_array, variable_names_labels.map(truncateStringForSelectionWindow), use_preselections, null);
const selected_variables = getElementsOfArrayBySelectedIndices(variable_array, selected_indices);
const selected_names_labels = getElementsOfArrayBySelectedIndices(variable_names_labels, selected_indices);
return { variables: selected_variables, names_labels: selected_names_labels };
}
// Generalization of selectOne() to accept an array of questions
function selectOneQuestion(message, question_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const question_names = getNamesOfQuestionsInArray(question_array).map(truncateStringForSelectionWindow);
const selected_index = selectOneVariableOrQuestionUsingPreselections(message, question_array, question_names, use_preselections, null);
return question_array[selected_index];
}
function selectOneQuestionByNameAndDataFile(message, question_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const question_names = getNamesOfQuestionsAndDataFilesInArray(question_array);
const selected_index = selectOneVariableOrQuestionUsingPreselections(message, question_array, question_names, use_preselections, null);
return question_array[selected_index];
}
// Generalization of selectOne() to accept an array of variables, allowing the user to
// make a selection by the variable labels
function selectOneVariableByLabel(message, variable_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const variable_labels = getLabelsOfVariablesInArray(variable_array);
const selected_index = selectOneVariableOrQuestionUsingPreselections(message, variable_array, variable_labels.map(truncateStringForSelectionWindow), use_preselections, null);
return variable_array[selected_index];
}
// Generalization of selectOne() to accept an array of variables, allowing the user to
// make a selection by the variable names
function selectOneVariableByName(message, variable_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const variable_names = getNamesOfVariablesInArray(variable_array);
const selected_index = selectOneVariableOrQuestionUsingPreselections(message, variable_array, variable_names.map(truncateStringForSelectionWindow), use_preselections, null);
return variable_array[selected_index];
}
function selectOneVariableByNameAndLabel(message, variable_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const variable_names = getNamesAndLabelsOfVariablesInArray(variable_array);
const selected_index = selectOneVariableOrQuestionUsingPreselections(message, variable_array, variable_names.map(truncateStringForSelectionWindow), use_preselections, null);
return variable_array[selected_index];
}
function selectOneVariableByQuestionNameAndLabel(message, variable_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const variable_names = getQuestionNamesAndLabelsOfVariablesInArray(variable_array);
const selected_index = selectOneVariableOrQuestionUsingPreselections(message, variable_array, variable_names.map(truncateStringForSelectionWindow), use_preselections, null);
return variable_array[selected_index];
}
function selectOneVariableByQuestionNameAndLabelAndDataFile(message, variable_array, use_preselections) {
if (use_preselections == undefined) {
use_preselections = true;
}
const variable_names = getQuestionNamesAndLabelsAndDataFilesOfVariablesInArray(variable_array);
const selected_index = selectOneVariableOrQuestionUsingPreselections(message, variable_array, variable_names.map(truncateStringForSelectionWindow), use_preselections, null);
return variable_array[selected_index];
}
// Generalization of selectOne() to accept an array of data files, allowing the user to
// make a selection by the data file names
function selectOneDataFile(message, datafile_array, default_index, form_options = {}) {
if (datafile_array.length === 0) {
throw new UserError(correctTerminology('At least one Data Set is required to use this script. ' +
'Please add a Data Set before re-running this script.'));
}
if (datafile_array.length === 1) {
return datafile_array[0];
}
const datafile_names = getNamesOfDataFilesInArray(datafile_array);
const selected_index = selectOne(message, datafile_names.map(truncateStringForSelectionWindow), undefined, default_index, form_options);
return datafile_array[selected_index];
}
// Generalization of selectMany() to array of data files, allowing the user
// to make selection by the current name of the file.
function selectManyDataFiles(message, datafile_array) {
const datafile_names = getNamesOfDataFilesInArray(datafile_array);
const selected_indices = selectMany(message, datafile_names.map(truncateStringForSelectionWindow));
const selected_datafiles = getElementsOfArrayBySelectedIndices(datafile_array, selected_indices);
const selected_names = getElementsOfArrayBySelectedIndices(datafile_names, selected_indices);
return { files: selected_datafiles, names: selected_names };
}
// If string is longer than 120 characters, truncate it so that it fits nicely on the window.
function truncateStringForSelectionWindow(string) {
const max_chars = 120;
if (string.length < max_chars) {
return string;
}
else {
return string.substring(0, max_chars - 3) + '...';
}
}
// If there is more than one data file in the current project, show the user a list
// and ask them which data files they want to use.
function dataFileSelection() {
const datafiles = project.dataFiles;
let selected_datafiles = datafiles;
if (datafiles.length > 1) {
selected_datafiles = selectManyDataFiles('Please choose which data files you would like to use:', datafiles).files;
}
return selected_datafiles;
}
function getGroupNameForTable(table, base_group_name) {
let group_item = table.group;
let name = table.name;
while (group_item && group_item.name != base_group_name) {
name = group_item.name + ": " + name;
group_item = group_item.group;
}
return name;
}
function getGroupNamesForTablesInArray(table_array, base_group_name) {
return table_array.map(t => getGroupNameForTable(t, base_group_name));
}
function getGroupNamesForTablesAndPlotsInArrayWithType(item_array, base_group_name) {
return item_array.map(t => {
return t.type + ": " + getGroupNameForTable(t, base_group_name);
});
}
function selectManyTablesWithGroupNames(message, group_item) {
const all_tables = [];
recursiveGetAllTablesInGroup(group_item, all_tables);
const table_names = getGroupNamesForTablesInArray(all_tables, group_item.name);
const selected_indices = selectMany(message, table_names);
const selected_tables = getElementsOfArrayBySelectedIndices(all_tables, selected_indices);
const selected_names = getElementsOfArrayBySelectedIndices(table_names, selected_indices);
return { tables: selected_tables, names: selected_names };
}
function selectManyTablesAndPlotsWithGroupNamesAndTypes(message, group_item) {
const all_items = [];
recursiveGetAllTablesAndPlotsInGroup(group_item, all_items);
const item_names = getGroupNamesForTablesAndPlotsInArrayWithType(all_items, group_item.name);
const selected_indices = selectMany(message, item_names);
const selected_items = getElementsOfArrayBySelectedIndices(all_items, selected_indices);
const selected_names = getElementsOfArrayBySelectedIndices(item_names, selected_indices);
return { items: selected_items, names: selected_names };
}
function selectOneTableWithGroupNames(message, group_item) {
const all_tables = [];
recursiveGetAllTablesInGroup(group_item, all_tables);
const table_names = getGroupNamesForTablesInArray(all_tables, group_item.name);
if (table_names.length == 0) {
return [];
}
const selected_index = selectOne(message, table_names);
return all_tables[selected_index];
}
function requestOneDataFileFromProject(last_as_default, message = '', form_options = {}) {
if (project.dataFiles.length === 0) {
throw new UserError(correctTerminology('You first must add a Data Set to your document to use this feature.'));
}
if (project.dataFiles.length === 1) {
return project.dataFiles[0];
}
else {
const user_selections = getAllUserSelections();
const selected_data_files = user_selections.selected_data_sets;
if (selected_data_files.length === 1) {
return selected_data_files[0];
}
const data_file_opts = project.dataFiles;
const msg = correctTerminology('There is more than one Data Set in your project. ' +
'Please select the Data Set to use:');
const default_selection = last_as_default ? data_file_opts.length - 1 : undefined;
return selectOneDataFile(message || msg, data_file_opts, default_selection, form_options);
}
}
function selectedTablesAndPlots(report) {
if (!report.selectedItems) {
return selectManyTablesAndPlotsWithGroupNamesAndTypes('Select tables and plots', report).items;
}
let selected_items = report.selectedItems();
if (selected_items.length == 0) {
if (confirm('You currently only have no tables and plots selected. Would you like to have this script run on everything in the Report?')) {
const selected_tables = [];
recursiveGetAllTablesAndPlotsInGroup(report, selected_tables);
selected_items = selected_tables;
}
}
else if (selected_items.length == 1) {
if (confirm('You currently only have one table/plot selected. Would you like to have this script run on everything in the Report?')) {
const selected_tables = [];
recursiveGetAllTablesAndPlotsInGroup(report, selected_tables);
selected_items = selected_tables;
}
}
const selected_tables_and_plots = [];
for (let i = 0; i < selected_items.length; i++) {
const item = selected_items[i];
if (isTable(item) || isPlot(item)) {
selected_tables_and_plots.push(item);
}
}
return selected_tables_and_plots;
}
// Place all tables in the group into the supplied table_array.
// This function will get tables from all subgroups of the
// specified group_item
function recursiveGetAllTablesInGroup(group_item, table_array) {
const cur_sub_items = group_item.subItems;
for (let j = 0; j < cur_sub_items.length; j++) {
const sub_item = cur_sub_items[j];
if (isReportGroup(sub_item)) {
recursiveGetAllTablesInGroup(sub_item, table_array);
}
else if (isTable(sub_item)) {
table_array.push(sub_item);
}
}
}
// Place all tables and plots in the group into the supplied table_array.
// This function will get tables from all subgroups of the
// specified group_item
function recursiveGetAllTablesAndPlotsInGroup(group_item, table_array) {
const cur_sub_items = group_item.subItems;
for (let j = 0; j < cur_sub_items.length; j++) {
const sub_item = cur_sub_items[j];
if (isReportGroup(sub_item)) {
recursiveGetAllTablesAndPlotsInGroup(sub_item, table_array);
}
else if (isTable(sub_item) || isPlot(sub_item)) {
table_array.push(sub_item);
}
}
}
// Place all pages in the group into the supplied table_array.
// This function will get pages from all subgroups of the
// specified group_item
function recursiveGetAllGroupsInGroup(group_item, group_array) {
const cur_sub_items = group_item.subItems;
for (let j = 0; j < cur_sub_items.length; j++) {
const sub_item = cur_sub_items[j];
if (isReportGroup(sub_item)) {
group_array.push(sub_item);
recursiveGetAllGroupsInGroup(sub_item, group_array);
}
}
}
// Place all items in the group into the supplied table_array.
// This function will get tables from all subgroups of the
// specified group_item
function recursiveGetAllItemsInGroup(group_item, item_array) {
const cur_sub_items = group_item.subItems;
for (let j = 0; j < cur_sub_items.length; j++) {
const sub_item = cur_sub_items[j];
if (isReportGroup(sub_item)) {
recursiveGetAllItemsInGroup(sub_item, item_array);
}
else {
item_array.push(sub_item);
}
}
}
// Try to add a data file to the project by asking the user to type the path.
// If the file can't be found then tell the user that the name is invalid
// and allow them to try again.
function filePromptWithVerification(message, override_file_name, options) {
let given_valid_file = false;
let file;
while (!given_valid_file) {
const filename = prompt(message);
try {
file = project.addDataFile(filename, override_file_name, options);
given_valid_file = true;
}
catch {
alert('Invalid file name!');
}
}
return file;
}
function oneOrMoreQuestions(questions) {
if (questions.length == 0) {
alert('Select one or more questions.');
return false;
}
else {
return true;
}
}
// Returns the indices of the objects in array_1 that are contained in array_2
// Can be arrays of questions or arrays of variables.
function indicesOfQObjectsInArray(array_1, array_2) {
if (array_1.length == 0 || array_2.length == 0) {
return [];
}
return array_2.map(function (obj_2) {
let ind = -1;
array_1.forEach(function (obj_1, index) {
if (ind == -1) {
if (obj_1.equals(obj_2)) {
ind = index;
}
}
});
return ind;
}).filter(function (x) {
return x >= 0;
});
}
function allQObjectsWithSameType(array) {
const target_type = array[0].type;
return array.every(x => x.type == target_type);
}
function selectOneVariableOrQuestionUsingPreselections(message, object_array, label_array, use_preselections, help_page) {
if (object_array == null || object_array.length == 0) {
throw new UserError('No questions of the appropriate type were found');
}
const object_type = object_array[0].type;
// No preselections for objects other than variables and questions
if (object_type != 'Variable' && object_type != 'Question') {
throw ('Expected array of variables or questions.');
}
if (!allQObjectsWithSameType(object_array)) {
throw ('Expected an array of objects with the same type.');
}
let selected_index;
if (project.report.selectedQuestions && use_preselections) {
let preselections;
if (object_type == 'Variable') {
preselections = project.report.selectedVariables();
}
else {
preselections = project.report.selectedQuestions();
}
let preselected_index;
if (preselections.length > 0) {
preselected_index = indicesOfQObjectsInArray(object_array, [preselections[0]])[0];
}
selected_index = selectOne(message, label_array, help_page, preselected_index);
}
else {
selected_index = selectOne(message, label_array, help_page);
}
return selected_index;
}
function selectManyVariablesOrQuestionsUsingPreselections(message, object_array, label_array, use_preselections, help_page) {
if (object_array == null || object_array.length == 0) {
throw new UserError('No questions of the appropriate type were found');
}
const object_type = object_array[0].type;
// No preselections for objects other than variables and questions
if (object_type != 'Variable' && object_type != 'Question') {
throw ('Expected array of variables or questions.');
}
if (!allQObjectsWithSameType(object_array)) {
throw ('Expected an array of objects with the same type.');
}
let selected_indices;
if (project.report.selectedQuestions && use_preselections) {
let preselections;
if (object_type == 'Variable') {
preselections = project.report.selectedVariables();
}
else {
preselections = project.report.selectedQuestions();
}
let preselected_indices = null;
if (preselections.length > 0) {
preselected_indices = indicesOfQObjectsInArray(object_array, preselections);
}
selected_indices = selectMany(message, label_array, help_page, preselected_indices);
}
else {
selected_indices = selectMany(message, label_array, help_page);
}
return selected_indices;
}
// Keep prompting the use for more entries until they enter a blank string.
// The message should tell them that they can stop if they enter a blank.
function promptUntilBlank(message) {
const answers = [];
// eslint-disable-next-line no-constant-condition
while (true) {
const new_answer = prompt(message);
if (new_answer == '') {
break;
}
else {
answers.push(new_answer);
}
}
return answers;
}
function promptForDKLabels() {
// Allow the user to enter additional strings for don't know labels
let extra_dk_strings = [];
if (askYesNo("This tool will check for standard 'Don't Know' style responses, including: " + _DK_STRINGS_GLOBAL.join(', ')
+ ", and labels that are NA. Do you wish to add additional 'Don't Know' response labels?")) {
extra_dk_strings = promptUntilBlank("Enter an extra 'Don't Know' label. If you have finished, click OK.");
}
extra_dk_strings = extra_dk_strings.map(s => s.toLowerCase());
return extra_dk_strings;
}
// Search through all the select tables and plots and return an array of objects, each of which has
// two properties:
// * question - The question object
// * item - the table or plot which contains the question
// * position - a string telling us if the questions is in Primary (blue), Secondary (brown)
// or Tertiary (second bule on some plots)
function getQuestionsSelectedInTables() {
const selected_questions = [];
const selected_items = project.report.selectedItems();
selected_items.forEach(function (item) {
if (isTable(item) || isPlot(item)) {
// Primary
if (item.primary) {
selected_questions.push({ question: item.primary, item: item, position: 'Primary' });
}
// Secondary
if (typeof item.secondary != 'string' && item.secondary != null) {
selected_questions.push({ question: item.secondary, item: item, position: 'Secondary' });
}
// Tertiary
if (item.tertiary != null) {
selected_questions.push({ question: item.tertiary, item: item, position: 'Tertiary' });
}
}
});
return selected_questions;
}
// Given an array of question pairs (originalQuestion, newQuestion),
// search through the tables and plots that were selected by the user
// at run time and replace originalQuestion with newQuestion wherever found.
// In case it is not a good idea to modify plots, pass replace_in_plots = false.
// If it is not appropriate for the user to select plots then it might be
// better to catch this earlier in the QScript.
function replaceQuestionsInSelectedTablesAndPlots(question_pairs, replace_in_plots) {
// Get all questions that are in any of the selected items,
// aong with the link back to the table;
const item_questions = getQuestionsSelectedInTables();
question_pairs.forEach(pair => {
// Get all item objects which contain the original question
// in this pair.
const relevant_items = item_questions.filter(obj => {
return obj.question.equals(pair.originalQuestion);
});
// Loop through those objbects and replace with new question
// where we can.
relevant_items.forEach(obj => {
const current_item = obj.item;
const current_type = current_item.type;
if (current_type == 'Table' || (current_type == 'Plot' && replace_in_plots)) {
if (obj.position == 'Primary') {
current_item.primary = pair.newQuestion;
}
else if (obj.position == 'Secondary') {
current_item.secondary = pair.newQuestion;
}
else if (obj.position == 'Tertiary') {
current_item.tertiary = pair.newQuestion;
}
}
});
});
}
// Return either the selected item if it is an R output, or search the current page for a single
// R output and return that. If you want to restrict to only returning ROutputs of a specific class
// then set required_classes to an array of acceptable class names of the class as a string. If you
// don't want to restrict, supply an empty array instead.
function getSelectedROutputFromPage(required_classes) {
// When a single item is identified, use this function to check it is valid and the right class
function checkLonelyROutput(selected_item) {
if (!checkROutputIsValid(selected_item)) {
return false;
}
if (required_classes.length > 0 && !required_classes.some(required_class => selected_item.outputClasses && selected_item.outputClasses.indexOf(required_class) > -1)) {
return false;
}
return true;
}
const selected_items = project.report.selectedRaw();
if (selected_items.length == 0) {
return null;
}
const selected_item = selected_items[0];
// If a single R Output is selected return it if it is the right class
// If it is the wrong class, return null
if (selected_items.length == 1 && selected_item.type == 'R Output') {
if (checkLonelyROutput(selected_item)) {
return selected_item;
}
else {
return null;
}
}
// Not an ROutput selected. Search the group/page to see if
// there is an ROutput
const selected_group = isReportGroup(selected_item) ? selected_item : selected_item.group;
let all_items_in_group = selected_group === null || selected_group === void 0 ? void 0 : selected_group.subItems;
if (all_items_in_group) {
all_items_in_group = all_items_in_group.filter(item => { var _a; return (_a = item.group) === null || _a === void 0 ? void 0 : _a.equals(selected_group); }); // Filter out items from further down the tree
all_items_in_group = all_items_in_group.filter(item => item.type === 'R Output');
}
let items_in_group = all_items_in_group;
// No R Outputs on the page
if (!items_in_group || items_in_group.length == 0) {
return null;
}
// If there is a single R Output on the page, check it and return it if it is good to use
if (items_in_group.length == 1) {
if (checkLonelyROutput(items_in_group[0])) {
return items_in_group[0];
}
else {
return null;
}
}
// Multiple R Outputs on the page, filter out any which are invalid or the wrong class
items_in_group = items_in_group.filter(item => item.error === null && item.outputClasses !== null);
if (items_in_group.length == 0) {
return null;
}
// If a class has been specified, filter to that class
if (required_classes.length > 0) {
items_in_group = items_in_group.filter(function (item) {
return required_classes.some(required_class => item.outputClasses && item.outputClasses.indexOf(required_class) > -1);
});
}
// If a single item remains, return it
if (items_in_group.length == 1) {
return items_in_group[0];
}
if (items_in_group.length > 1) {
log('There are multiple R Outputs available on this page, but this option requires a single R Output. ' +
'Click the item you want to use and run this option again.');
}
return null;
}
function questionsInSelectedItems() {
const questions_in_selected_items = [];
project.report.selectedItems().forEach(function (item) {
var _a, _b;
if (isTable(item) || isPlot(item)) {
if (((_a = item.primary) === null || _a === void 0 ? void 0 : _a.type) == 'Question') {
questions_in_selected_items.push(item.primary);
}
if (typeof item.secondary !== 'string' && ((_b = item.secondary) === null || _b === void 0 ? void 0 : _b.type) == 'Question') {
questions_in_selected_items.push(item.secondary);
}
if (item.tertiary != null && item.tertiary.type == 'Question') {
questions_in_selected_items.push(item.tertiary);
}
}
});
return questions_in_selected_items;
}
// Returns (all are arrays of Q objects):
// - selected_data_sets: Data Sources currently selected
// - selected_questions: All questions directly selected by the user
// - selected_pages: All pages directly selected by the user (includes sub pages)
// - selected_tables: All tables directly selected by the user
// - selected_plots: All Q plots, excluding word clouds, directly selected by the user
// - selected_word_clouds: All word clouds directlt selected by the user
// - selected_r_outputs: All Calculations directly selected by the user
// - selected_top_level_pages: All selected pages at the top level only (that is, does not include subpages of selected pages)
// - selected_variables: All variables directly selected by the user
// - single_variable_questions_selected: All directly selected questions that only contain a single variable
// - multiple_variable_questions_selected: All directly selected questions that contain multiple variables
// - implicitly_selected_items: All items, including tables, plots, R outputs implicitly selected by the user
// - implicitly_selected_tables: Tables selected implicitly by the user
// - implicitly_selected_plots: Plots implicitly selected by the user
// - implicitly_selected_word_clouds: Word clouds implictly selected by the user
// - implicitly_selected_r_outputs: Calculations implicitly selected by the user
// - questions_in_selected_tables: Questions present in the tables which are directly selected by the user
// - questions_in_rows_of_selected_tables: Questions present in the rows of tables which are directly selected by the user
// - questions_in_columns_of_selected_tables: Questions present in the columns of tables which are directly selected by the user
// - questions_in_selected_plots: Questions present in the Q plots which are directly selected by the user
// - v_and_q_selected_questions: Questions selected in the Variables and Questions tab. Will be empty if the user is not in this tab
// - v_and_q_selected_variables: Variables selected in the Variables and Questions tab. Will be empty if the user is not in this tab
// - current_page: The page the user is currently on
// Function to obtain all user selections.
function getAllUserSelections() {
const is_displayr = !!Q.isOnTheWeb && Q.isOnTheWeb();
let selected_questions = project.report.selectedQuestions();
const selected_raw = project.report.selectedRaw();
const selected_variables = project.report.selectedVariables();
const implicitly_selected_items = project.report.selectedItems().filter(function (item) { return ['Table', 'Plot', 'WordCloud', 'R Output'].includes(item.type); });
const selected_tables = [];
const selected_plots = [];
const selected_word_clouds = [];
const selected_r_outputs = [];
selected_raw.forEach(item => {
switch (item.type) {
case 'Table':
selected_tables.push(item);
break;
case 'Plot':
selected_plots.push(item);
break;
case 'Word Cloud':
selected_word_clouds.push(item);
break;
case 'R Output':
selected_r_outputs.push(item);
break;
}
});
const selected_items = selected_raw.filter(function (item) { return ['Table', 'Plot', 'WordCloud', 'R Output'].includes(item.type); });
const implicitly_selected_tables = [];
const implicitly_selected_plots = [];
const implicitly_selected_word_clouds = [];
const implicitly_selected_r_outputs = [];
implicitly_selected_items.forEach(item => {
switch (item.type) {
case 'Table':
implicitly_selected_tables.push(item);
break;
case 'Plot':
implicitly_selected_plots.push(item);
break;
case 'Word Cloud':
implicitly_selected_word_clouds.push(item);
break;
case 'R Output':
implicitly_selected_r_outputs.push(item);
break;
}
});
const selected_top_level_pages = selected_raw.filter(item => item.type == 'ReportGroup');
// Collect all subpages of top level selected pages
const selected_pages = [];
const selected_page_guids = [];
function getSubPages(page, known_pages, known_page_guids) {
const current_guid = page.guid;
if (known_page_guids.indexOf(current_guid) == -1) {
known_pages.push(page);
known_page_guids.push(current_guid);
const subpages = page.subItems.filter(item => isReportGroup(item));
subpages.forEach(page => getSubPages(page, known_pages, known_page_guids));
}
}
selected_top_level_pages.forEach(page => getSubPages(page, selected_pages, selected_page_guids));
// Identify questions that are selected implicitly within the selected outputs.
// Questions in tables
const questions_in_selected_tables = [];
const questions_in_rows_of_selected_tables = [];
const questions_in_columns_of_selected_tables = [];
selected_tables.forEach(table => {
if (table.primary != null) {
questions_in_selected_tables.push(table.primary);
questions_in_rows_of_selected_tables.push(table.primary);
}
if (table.secondary != null && typeof table.secondary != 'string') {
questions_in_selected_tables.push(table.secondary);
questions_in_columns_of_selected_tables.push(table.secondary);
}
});
// Questions in plots
const questions_in_selected_plots = [];
selected_plots.forEach(plot => {
if (plot.primary != null) {
questions_in_selected_plots.push(plot.primary);
}
if (plot.secondary != null && typeof plot.secondary != 'string') {
questions_in_selected_plots.push(plot.secondary);
}
if (plot.tertiary != null && typeof plot.tertiary != 'string') {
questions_in_selected_plots.push(plot.tertiary);
}
});
// Remove from selected_questions things which are selected in tables or plots
// The intention of selected_questions is to contain questions explicitly selected
// by the user in Data Sources
const implicitly_selected_questions = questions_in_selected_tables.concat(questions_in_selected_plots);
const implicitly_selected_question_guids = implicitly_selected_questions.map(q => q.guid);
if (is_displayr) {
selected_questions = selected_questions.filter(q => implicitly_selected_question_guids.indexOf(q.guid) == -1);
}
// Sort questions into singles and multis
const single_variable_questions_selected = selected_questions.filter(q => q.variables.length == 1);
const multiple_variable_questions_selected = selected_questions.filter(q => q.variables.length > 1);
const single_variable_questions_implicitly_selected = implicitly_selected_questions.filter(q => q.variables.length == 1);
const multiple_variable_questions_implicitly_selected = implicitly_selected_questions.filter(q => q.variables.length > 1);
// Check in Q for whether have selections in the variables
// and questions tab.
// These will be empty if the user has not made selections
// outside of the outputs tab.
const v_and_q_selected_questions = selected_questions.filter(q => implicitly_selected_question_guids.indexOf(q.guid) == -1);
const implicitly_selected_variables = implicitly_selected_questions.map(q => q.variables).flat();
const implicitly_selected_variables_guids = implicitly_selected_variables.map(v => v.guid);
const v_and_q_selected_variables = selected_variables.filter(v => implicitly_selected_variables_guids.indexOf(v.guid) == -1);
// bugdup!23045 selectedDataSets() not available in Q5.9-fixes (QStable at 2021-09-13)
let selected_data_sets;
if (project.report.selectedDataSets) {
selected_data_sets = project.report.selectedDataSets();
}
else {
selected_data_sets = selected_questions.concat(questions_in_selected_tables)
.concat(questions_in_selected_plots)
.map(q => q.dataFile);
const file_names = selected_data_sets.map(data_file => data_file.name);
selected_data_sets = selected_data_sets.filter((data_file, idx) => file_names.indexOf(data_file.name) === idx);
}
const current_page = project.currentPage ? project.currentPage() : null;
return {
selected_data_sets: selected_data_sets,
implicitly_selected_items: implicitly_selected_items,
selected_questions: selected_questions,
selected_pages: selected_pages,
selected_tables: selected_tables,
selected_plots: selected_plots,
selected_word_clouds: selected_word_clouds,
selected_r_outputs: selected_r_outputs,
selected_items: selected_items,
selected_top_level_pages: selected_top_level_pages,
selected_variables: selected_variables,
single_variable_questions_selected: single_variable_questions_selected,
multiple_variable_questions_selected: multiple_variable_questions_selected,
implicitly_selected_tables: implicitly_selected_tables,
implicitly_selected_plots: implicitly_selected_plots,
implicitly_selected_word_clouds: implicitly_selected_word_clouds,
implicitly_selected_r_outputs: implicitly_selected_r_outputs,
implicitly_selected_questions: implicitly_selected_questions,
single_variable_questions_implicitly_selected: single_variable_questions_implicitly_selected,
multiple_variable_questions_implicitly_selected: multiple_variable_questions_implicitly_selected,
questions_in_selected_tables: questions_in_selected_tables,
questions_in_rows_of_selected_tables: questions_in_rows_of_selected_tables,
questions_in_columns_of_selected_tables: questions_in_columns_of_selected_tables,
questions_in_selected_plots: questions_in_selected_plots,
current_page: current_page,
v_and_q_selected_questions: v_and_q_selected_questions,
v_and_q_selected_variables: v_and_q_selected_variables
};
}
// Function to determine if at least one object of the
// desired type has been selected by the user
function oneOrMoreSelected(user_selections, type, implicitly_selected_allowed) {
const us = user_selections;
const isa = implicitly_selected_allowed;
switch (type) {
case 'Group': return us.selected_pages.length > 0;
case 'Page': return us.selected_pages.length > 0;
case 'Table': return (us.selected_tables.length +
(isa ? us.implicitly_selected_tables.length : 0)) > 0;
case 'R Output': return (us.selected_r_outputs.length +
(isa ? us.implicitly_selected_r_outputs.length : 0)) > 0;
case 'Plot': return (us.selected_plots.length +
(isa ? us.implicitly_selected_plots.length : 0)) > 0;
case 'Question': return us.selected_questions.length > 0;
case 'Variable Set': return us.selected_variables.length > 0;
case 'Variable': return us.selected_variables.length > 0;
case 'WordCloud': return (us.selected_word_clouds.length +
(isa ? us.implicitly_selected_word_clouds.length : 0)) > 0;
}
return false;
}
// Function to determine if at least one object of the
// desired type have been directly selected by the user
function NOrMoreSelected(user_selections, type, n, implicitly_selected_allowed) {
const us = user_selections;
const isa = implicitly_selected_allowed;
switch (type) {
case 'Group': return us.selected_pages.length >= n;
case 'Page': return us.selected_pages.length >= n;
case 'Top Pages': return us.selected_top_level_pages.length >= n;
case 'Table': return us.selected_tables.length +
(isa ? us.implicitly_selected_tables.length : 0) >= n;
case 'R Output': return us.selected_r_outputs.length +
(isa ? us.implicitly_selected_r_outputs.length : 0) >= n;
case 'Plot': return us.selected_plots.length +
(isa ? us.implicitly_selected_plots.length : 0) >= n;
case 'Question': return us.selected_questions.length >= n;
case 'Variable Set': return us.selected_variables.length >= n;
case 'Variable': return us.selected_variables.length >= n;
case 'WordCloud': return us.selected_word_clouds.length +
(isa ? us.implicitly_selected_word_clouds.length : 0) >= n;
case 'Data Set': return us.selected_data_sets.length >= n;
}
return false;
}
// Function to determine if exactly n items of the
// desired type have been directly selected by the user
function exactlyNSelected(user_selections, type, n, implicitly_selected_allowed) {
const us = user_selections;
const isa = implicitly_selected_allowed;
switch (type) {
case 'Group': return us.selected_pages.length == n;
case 'Page': return us.selected_pages.length == n;
case 'Top Pages': return us.selected_top_level_pages.length == n;
case 'Table': return us.selected_tables.length +
(isa ? us.implicitly_selected_tables.length : 0) == n;
case 'R Output': return us.selected_r_outputs.length +
(isa ? us.implicitly_selected_r_outputs.length : 0) == n;
case 'Plot': return us.selected_plots.length +
(isa ? us.implicitly_selected_plots.length : 0) == n;
case 'Question': return us.selected_questions.length == n;
case 'Variable Set': return us.selected_variables.length == n;
case 'Variable': return us.selected_variables.length == n;
case 'WordCloud': return us.selected_word_clouds.length +
(isa ? us.implicitly_selected_word_clouds.length : 0) == n;
case 'Data Set': return us.selected_data_sets.length == n;
}
return false;
}
// Returns true if all top-level selected pages are in the same folder, otherwise false.
function selectedPagesAreInSameFolder(user_selections) {
const selected_top_level_pages = user_selections.selected_top_level_pages;
const parents = selected_top_level_pages.map(page => page.parentGroup());
return parents.every(grp => parents[0] == null ? grp == null : grp && parents[0].equals(grp));
}
// This function asks the user to select questions as input for the Qscript
// It can be used in either Q or Displayr. The allowed structures should be
// specified as VariableSetStructures. In Q, the error message will automatically
// convert this to QuestionTypes.
// In Displayr the function will identify the selected questions and will stop if
// those questions are not appropriate. In Q, the function will prompt the user
// to guide them to an appropriate selection if their initial selection is not
// appropriate.
// If the user is only allowed to select one question, set 'single' to true.
function selectInputQuestions(allowed_structures, single, select_questions, add_levels) {
var _a;
if (typeof single === 'undefined') {
single = false;
}
if (typeof select_questions == 'undefined') {
select_questions = false;
}
if (typeof add_levels == 'undefined') {
add_levels = false;
}
if (!requireDataFile()) {
return false;
}
const is_displayr = inDisplayr();
let structure_name = is_displayr ? "variable set" : "question";
if (!single) {
structure_name = structure_name + "s";
}
const user_selections = getAllUserSelections();
let selected_questions = user_selections.selected_questions;
if (selected_questions.length > 0) {
// Remove and silently ignore questions that do not have allowed structures
const sorted_selection = splitArrayIntoApplicableAndNotApplicable(selected_questions, function (q) { return allowed_structures.indexOf(q.variableSetStructure) != -1 && !q.isBanner; });
selected_questions = sorted_selection.applicable;
}
if (single && selected_questions.length == 1) {
return (selected_questions[0]);
}
if (selected_questions.length == 0 || single) {
// No pop-ups in Displayr, just tell them what to do and then exit
if (is_displayr) {
log("Select " + (single ? "exactly one" : "one or more ") + printTypesString(allowed_structures) + " " + structure_name + ".");
return false;
}
if (fileFormatVersion() < 13.05) {
log("This QScript is not supported in this version of Q. Please use release version 5.4.1.0 or later to use this QScript.");
return false;
}
const data_file = requestOneDataFileFromProject();
let candidate_questions = getAllQuestionsByStructures([data_file], allowed_structures);
if (candidate_questions.length === 0) {
if (!is_displayr) {
convertStructureToType(allowed_structures);
}
log("No appropriate " + printTypesString(allowed_structures) + " " + structure_name + " found in the data file. " +
"Please choose another data file or create " + structure_name + " with this structure.");
return false;
}
// This section gives the user the option of only showing scale identified questions
// so the user can have a pre-filtered selection. Used for the topAndBottomBoxNetCreator function
if (select_questions) {
candidate_questions = (_a = select_questions(data_file)) !== null && _a !== void 0 ? _a : candidate_questions;
}
// This section adds the lowest and highest value and labels to the question label
// so the user can inspect it during selection. Used for the topAndBottomBoxNetCreator function
if (add_levels) {
const question_label_strings = candidate_questions.map(q => {
const highest_and_lowest_label = getHighestAndLowestValueAndLabel(q);
return truncateStringForSelectionWindow(q.name) + " ("
+ highest_and_lowest_label.lowest + " ... "
+ highest_and_lowest_label.highest + ")";
});
const selected_indices = selectMany("Please choose which " + structure_name + " you want to add NETs to:\r\n(highest and lowest value labels are shown in brackets)", question_label_strings);
selected_questions = getElementsOfArrayBySelectedIndices(candidate_questions, selected_indices);
}
else {
if (single) {
selected_questions = selectOneQuestion("Select " + structure_name + ":", candidate_questions, true);
}
else {
selected_questions = selectManyQuestions("Select " + structure_name + ":", candidate_questions, true).questions;
}
}
}
if (isArray(selected_questions) && selected_questions.length === 0) {
return false;
}
return selected_questions;
}
// Function below to be used as a filter for R outputs that are considered Tables
const VALID_ROUTPUT_FOR_TABLE_CLASSES = ['matrix', 'array', 'data.frame', 'table',
'integer', 'numeric', 'character', 'factor', 'logical'];
function validTableOrTableLikeROutput(item) {
return (isTable(item) && item.primary !== null) ||
(isROutput(item) && item.error === null &&
item.outputClasses && item.outputClasses.filter(c => VALID_ROUTPUT_FOR_TABLE_CLASSES.includes(c)).length > 0);
}
function checkROutputIsValid(selected_item) {
if (Q.fileFormatVersion() <= 9.23) {
return true; //We cannot perform this check in Q5.0.3 and earlier
}
const web_mode = (!!Q.isOnTheWeb && Q.isOnTheWeb());
if (isROutput(selected_item)) {
if (selected_item.error === null && selected_item.outputClasses === null) {
// Get user input
if (!web_mode) {
const recalculate = confirm("The selected R Output must be calculated before continuing.\nWould you like to do so now?");
if (recalculate == true) {
selected_item.update();
}
else {
log("The selected R Output has not been calculated.");
return false;
}
}
else {
log("The selected R Output has not been calculated. It must be calculated before continuing.");
return false;
}
}
if (selected_item.error !== null) {
log("There is an error with your R Output that must be fixed first: " + selected_item.error);
return false;
}
}
else {
log("The selected item is not an R Output.");
return false;
}
return true;
}
function requireOneValidROutputByClass(required_classes) {
var _a;
const selections = getAllUserSelections();
const invalid_input_msg = "Please select a single output from " +
correctTerminology("Anything > Advanced Analysis > ") +
anythingMenuLocation(required_classes) +
" and then rerun this feature.";
if (!exactlyNSelected(selections, "R Output", 1, false)) {
throw new UserError(invalid_input_msg);
}
const r_output = selections.selected_r_outputs.length > 0 ?
selections.selected_r_outputs[0] : selections.implicitly_selected_r_outputs[0];
if (!checkROutputIsValid(r_output)) {
throw new UserError("There is an error with your selected model. " +
"Please fix the issue in your selection before rerunning this feature.");
}
else if (required_classes.length > 0) {
if (!((_a = r_output.outputClasses) === null || _a === void 0 ? void 0 : _a.some(cname => required_classes.includes(cname))) &&
!validOldClassFormatOutput(r_output, required_classes)) {
throw new UserError(invalid_input_msg);
}
}
return r_output;
}
// extra checks to support older Choice Model, MaxDiff, and Regression
// outputs that only appended a single class (e.g. "FitChoice") to their outputs
function validOldClassFormatOutput(routput, required_classes) {
const output_classes = routput.outputClasses;
if ((output_classes === null || output_classes === void 0 ? void 0 : output_classes.length) !== 1) {
// All newly created AA outputs have multiple classes
return false;
}
const output_class = output_classes[0];
let algorithm;
if (output_class === "FitMaxDiff" && /^FitMaxDiff/.test(required_classes[0])) {
algorithm = routput.data.get(["algorithm"])[0];
if (required_classes.some(cls => /HB$/.test(cls)) && /^HB/.test(algorithm)) {
return true;
}
else if (required_classes.some(cls => /LCA$|MNL$/.test(cls)) &&
algorithm === "Default") {
return true;
}
}
else if (output_class === "FitChoice" && /^FitChoice/.test(required_classes[0])) {
algorithm = routput.data.get(["algorithm"])[0];
if (required_classes.some(cls => /HB$/.test(cls)) && /^HB/.test(algorithm)) {
return true;
}
else if (required_classes.some(cls => /LCA$|MNL$/.test(cls)) &&
algorithm === "LCA") {
return true;
}
}
else if (output_class === "Regression") {
const rtype = routput.data.get(["type"])[0];
if (required_classes[0] === "LinearRegression" && rtype === "Linear") {
return true;
}
else if (required_classes[0] === "Regression") {
return true;
}
}
return false;
}
function anythingMenuLocation(classes) {
switch (classes[0]) {
case "ChoiceModelDesign": return "Choice Modeling > Experimental Design";
case "FitChoiceLCA": return "Choice Modeling > Latent Class Analysis";
case "FitChoiceHB": return "Choice Modeling > Hierarchical Bayes";
case "FitChoice": return "Choice Modeling (e.g. Hierarchical Bayes)";
case "FitMaxDiffLCA": return "Marketing > MaxDiff > Latent Class Analysis";
case "FitMaxDiffHB": return "Marketing > MaxDiff > Hierarchical Bayes";
case "FitMaxDiff": return "Marketing > MaxDiff (e.g. Hierarchical Bayes)";
case "CorrespondenceAnalysis": return "Dimension Reduction > " +
"Correspondence Analysis of a Table";
case "LDA": return "Machine Learning > Linear Discriminant Analysis";
case "MachineLearning": return "Machine Learning (e.g. Random Forest)";
case "LinearRegression": return "Regression > Linear Regression";
case "Regression": return "Regression (e.g. Linear Regression)";
}
}
function onPages(description) {
const can_edit_pages = Q.userCanEditPages && Q.userCanEditPages();
if (!can_edit_pages) {
// if the user can't edit pages, then add as an output (false)
return false;
}
const options = [
`View and modify only the ${description}. You can still convert them to pages later.`,
`Create ${description} on pages, where you may add more objects to the page.`
];
const result = selectOne("Do you want to:", options);
return result === 1 ? true : false;
}
/** find a question by guid in all data files*/
function getQuestionById(question_guid) {
for (let i = 0; i < project.dataFiles.length; i++) {
const data_file = project.dataFiles[i];
if (!data_file || !data_file.questions) {
continue;
}
const question = data_file.questions.find(q => q.guid === question_guid);
if (question) {
return question;
}
}
return undefined;
}