PG.pl - Provides core Program Generation Language functionality.
In a PG problem:
DOCUMENT(); # should be the first statment in the problem
loadMacros(.....); # (optional) load other macro files if needed.
HEADER_TEXT(...); # (optional) used only for inserting javaScript into problems.
TEXT( # insert text of problems
"Problem text to be displayed. ",
"Enter 1 in this blank:",
ans_rule(30) # ans_rule(30) defines an answer blank 30 characters long.
# It is defined in PGbasicmacros.pl.
);
ANS(answer_evalutors); # see PGanswermacros.pl for examples of answer evaluatiors.
ENDDOCUMENT() # must be the last statement in the problem
This file provides the fundamental macros that define the PG language. It maintains a problem's text, header text, and answers:
Problem text: The text to appear in the body of the problem. See "TEXT" below.
Header text: When a problem is processed in an HTML-based display mode, this variable can contain text that the caller should place in the HEAD of the resulting HTML page. See "HEADER_TEXT" below.
Implicitly labeled answers: Answers that have not been explicitly assigned names, and are associated with their answer blanks by the order in which they appear in the problem. These types of answers are designated using the "ANS" macro.
Explicitly labeled answers: Answers that have been explicitly assigned names with the "NAMED_ANS" macro, or a macro that uses it. An explicitly labeled answer is associated with its answer blank by name.
"Extra" answers: Names of answer blanks that do not have a 1-to-1 correspondence to an answer evaluator. For example, in matrix problems, there will be several input fields that correspond to the same answer evaluator.
This file is automatically loaded into the namespace of every PG problem. The macros within can then be called to define the structure of the problem.
DOCUMENT()
should be the first executable statement in any problem. It initializes variables and defines the problem environment.
TEXT()
concatenates its arguments and appends them to the stored problem text string. It is used to define the text which will appear in the body of the problem. It can be used more than once in a file. For example,
TEXT("string1", "string2", "string3");
This macro has no effect if rendering has been stopped with the STOP_RENDERING()
macro.
This macro defines text which will appear in the problem. All text must be passed to this macro, passed to another macro that calls this macro, or included via a BEGIN_TEXT/END_TEXT or BEGIN_PGML/END_PGML block which uses this macro internally. No other statements in a PG file will directly appear in the output. Think of this as the "print" function for the PG language.
Spaces are placed between the arguments during concatenation, but no spaces are introduced between the existing content of the header text string and the new content being appended.
HEADER_TEXT()
concatenates its arguments and appends them to the stored header text string. It can be used more than once in a file. For example,
HEADER_TEXT("string1", "string2", "string3");
The macro is used for material which is destined to be placed in the HEAD of the page when in HTML mode, such as JavaScript code.
Spaces are placed between the arguments during concatenation, but no spaces are introduced between the existing content of the header text string and the new content being appended.
DEPRECATED
Content added by this method is appended just after the page head. This method should no longer be used. There is no valid reason to add content after the page head, and not in the problem itself.
Valid HTML language codes are expected, but a region code or other settings may be included. See https://www.w3.org/International/questions/qa-choosing-language-tags.
SET_PROBLEM_LANGUAGE($language)
Example language codes: en-US, en-UK, he-IL
Some special language codes (e.g. zh-Hans) are longer. See the following references.
https://www.w3.org/International/questions/qa-lang-2or3.en.html
http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
There is a tester located at https://r12a.github.io/app-subtags/
Call SET_PROBLEM_TEXTDIRECTION
to set the HTML dir
attribute to be applied to the div
element containing the problem.
SET_PROBLEM_TEXTDIRECTION($dir)
Only valid settings for the HTML dir
attribute are permitted.
dir="ltr|rtl|auto"
See https://www.w3schools.com/tags/att_global_dir.asp.
It is likely that only problems written in RTL scripts will need to call the following function to set the base text direction for the problem.
Note the flag may not be set, and then the default behavior will be used.
Request that the problem HTML page also include additional CSS files from the pg/htdocs
directory or from an external location.
ADD_CSS_FILE($file, $external);
If external is 1, it is assumed the full URL is provided. If external is 0 or not given, then file will be served from the pg/htdocs
directory (if found).
For example:
ADD_CSS_FILE("css/rtl.css");
ADD_CSS_FILE("https://external.domain.com/path/to/file.css", 1);
Request that the problem HTML page also include additional JavaScript files from the pg/htdocs
directory or from an external location.
ADD_JS_FILE($file, $external);
If external is 1, it is assumed the full URL is provided. If external is 0 or not given, then file name will be served from the pg/htdocs
directory (if found).
Additional attributes can be passed as a hash reference in the optional third argument. These attributes will be added as attributes to the script tag.
For example:
ADD_JS_FILE("js/Base64/Base64.js");
ADD_JS_FILE("https://cdn.geogebra.org/apps/deployggb.js", 1);
ADD_JS_FILE("js/GraphTool/graphtool.js", 0, { id => "gt_script", defer => undef });
Associates answer names with answer evaluators. If the given anwer name has a response group in the PG_ANSWERS_HASH, then the evaluator is added to that response group. Otherwise the name and evaluator are added to the hash of explicitly named answer evaluators. They will be paired with exlplicitly named answer rules by name. This allows pairing of answer evaluators and answer rules that may not have been entered in the same order.
An example of the usage is:
TEXT(NAMED_ANS_RULE("name1"), NAMED_ANS_RULE("name2"));
NAMED_ANS(name1 => answer_evaluator1, name2 => answer_evaluator2);
Alias for NAMED_ANS
Registers answer evaluators to be implicitly associated with answer names. If there is an answer name in the implicit answer name stack, then a given answer evaluator will be paired with the first name in the stack. Otherwise the evaluator will be pushed onto the implicit answer evaluator stack. This is the standard method for entering answers.
An example of the usage is:
TEXT(ans_rule(), ans_rule(), ans_rule());
ANS($answer_evaluator1, $answer_evaluator2, $answer_evaluator3);
In the above example, answer_evaluator1
will be associated with the first answer rule, answer_evaluator2
with the second, and answer_evaluator3
with the third. In practice, the arguments to ANS()
will usually be calls to an answer evaluator generator such as the cmp()
method of MathObjects or the num_cmp()
macro in PGanswermacros.pl.
Records the name for an answer blank. Used internally by PGbasicmacros.pl to record the order of answer blanks. All answer blanks must eventually be recorded via this method.
RECORD_ANS_NAME('name', 'VALUE');
Records the name for an answer blank that is implicitly named. Used internally by PGbasicmacros.pl to record the order of answer blanks that are implicitly nameed. This must also be called by a macro for answer blanks created by it that need to be implicitly named.
RECORD_IMPLICIT_ANS_NAME('name');
Generates an anonymous answer name from the internal count. This method takes no arguments.
Generates an answer name from the supplied answer number, but does not add it to the list of implicitly-named answers. This is deprecated, and most likely will not give something useful.
ANS_NUM_TO_NAME($num);
Stores the name of a form field in the "extra" answers list. This is used to keep track of answer blanks that are not associated with an answer evaluator.
RECORD_FORM_LABEL("name");
Generate an additional answer name for an existing array (vector) element and add it to the list of "extra" answers.
NEW_ANS_ARRAY_NAME_EXTENSION($row, $col);
When PG problems are evaluated, the result of evaluating the entire problem is interpreted as the return value of ENDDOCUMENT()
. Furthermore, a post processing hook is added that injects feedback into the problem text. Therefore, ENDDOCUMENT()
must be the last executable statement of every problem. It can only appear once. It returns a list consisting of:
A reference to a string containing the rendered text of the problem.
A reference to a string containing text to be placed in the HEAD block when in and HTML-based mode (e.g. for JavaScript).
A reference to a string containing text to be placed immediately after the HEAD block when in and HTML-based mode.
A reference to the hash mapping answer names to answer evaluators.
A reference to a hash containing various flags. This includes the following flags:
showPartialCorrectAnswers
: determines whether students are told which of their answers in a problem are wrong.
recordSubmittedAnswers
: determines whether students submitted answers are saved.
refreshCachedImages
: determines whether the cached image of the problem in typeset mode is always refreshed (i.e. setting this to 1 means cached images are not used).
solutionExits
: indicates the existence of a solution.
hintExits
: indicates the existence of a hint.
comment
: contents of COMMENT commands if any.
PROBLEM_GRADER_TO_USE
: a reference to the chosen problem grader. ENDDOCUMENT
chooses the problem grader as follows:
If a problem grader has been chosen in the problem by calling install_problem_grader(\&grader)
, it is used.
Otherwise, if the PROBLEM_GRADER_TO_USE
PG environment variable contains a reference to a subroutine, it is used.
Otherwise, if the PROBLEM_GRADER_TO_USE
PG environment variable contains the string std_problem_grader
or the string avg_problem_grader
, &std_problem_grader
or &avg_problem_grader
are used. These graders are defined in PGanswermacros.pl.
Otherwise, the PROBLEM_GRADER_TO_USE
flag will contain an empty value and the PG translator should select &std_problem_grader
.
A reference to the PGcore
object for this problem.
The post processing hook added in this method adds a feedback button for each answer response group that when clicked opens a popover containing feedback for the answer. A result class is also added to each feedbackElement
(see this option below) for coloring answer rules via CSS. In addition visually hidden spans are added that provide feedback for screen reader users. Each feedbackElement
will be aria-describedby
these spans.
When and what feedback is shown is determined by translator options described in "OPTIONS" in WeBWorK::PG as well as options described below. The hook handles standard answer types effectively, but macros that add special answer types and in some case problems (particularly those that use MultiAnswer
questions with singleResult
true) may need to help the method for proper placement of the feedback button and other aspects of feedback.
There are several options that can be modified, and a few different ways to make these modifications. Unfortunately, this is perhaps a little bit complicated to understand, and that really can not be helped. The reason for this is the extremely loose connection between answer rules, answer labels, and answer evaluators in PG.
How these options are set can be controlled in three ways.
First, an answer hash can have the feedback_options
key set to a CODE reference. If this is the case, then the subroutine referenced by this key will be called and passed the answer hash itself, a reference to the hash of options described below (any of which can be modified by this subroutine), and a Mojo::DOM object containing the problem text. Note that if this method sets the insertElement
option, then the other ways of controlling how these options are set will not be used.
Second, an element can be added to the DOM that contains an answer rule that has the class ww-feedback-container
, and if that answer rule is initially chosen to be the insertElement
and that is not set by the feedback_options
method, then this added element will replace it as the insertElement
.
Third, data attributes may be added to elements in the DOM will affect where the feedback button will be placed. The following data attributes are honored.
data-feedback-insert-element
: If an element in the DOM has this data attribute and the value of this attribute is the answer name (or label), then the element that has this data attribute will be used for the insertElement
option described below.
data-feedback-insert-method
: If the insertElement
is not set by the feedback_options
method of the answer hash, and the insertElement
also has this attribute, then the value of this attribute will be used for the insertMethod
option described below.
data-feedback-btn-add-class
: If the insertElement
is not set by the feedback_options
method of the answer hash, and the insertElement
also has this attribute, then the value of this attribute will be used for the btnAddClass
option described below.
The options that can be modified are as follows.
resultTitle
: This is the title that is displayed in the feedback popover for the answers in the response group. By default this is "Answer Preview", "Correct", "Incorrect", or "n% correct", depending on the status of the answer and the type of submission. Those strings are translated via maketext
. Usually this should not be changed, but in some cases the default status titles are not appropriate for certain types of answers. For example, the PGessaymacros.pl macros changes this to "Ungraded" for essay answers.
resultClass
: This is the CSS class that is added to each answer input in the response group. By default it is set to the empty string, "correct", "incorrect", or "partially-correct" depending on the status of the answer and the type of submission.
btnClass
: This is the bootstrap button class added to the feedback button. By default it is "btn-info", "btn-success", "btn-danger", or "btn-warning" depending on the status of the answer and the type of submission.
btnAddClass
: This is a string containing additional space separated CSS classes to add to the feedback button. This is "ms-2" by default. Macros can change this to affect positioning of the button. This generally should not be used to change the appearance of the button.
feedbackElements
: This is a Mojo::Collection of elements in the DOM to which the feedback resultClass
and aria-describedby
attribute will be added. By default this is all elements in the DOM that have a name in the list of response labels for the response group. Note that for radio buttons and checkboxes, only the checked elements will be in this collection by default.
insertElement
: This is the element in the DOM to insert the feedback button in or around. How the element is inserted is determined by the insertMethod
option. How this option is set is slightly complicated. First, if this option is set by the answer hash feedback_options
method, then that is used. If the feedback_options
method does not exist or does not set this option, then initially the last feedbackElement
is used for this. However, if that last feedbackElement
is contained in another DOM element that has the ww-feedback-container
class, then that is used for this instead. If such a container is not found and there is an element in the DOM that has the data-feedback-insert-element
attribute set whose value is equal to the name of this last feedbackElement
, then that element is used for the insertElement
instead. Finally, if the insertElement
determined as just described happens to be a radio button or checkbox, then the insertElement
will instead be the parent of the radio button or checkbox (which will hopefully be the label for that input).
insertMethod
: The Mojo::DOM method to use to insert the feedback button relative to the insertElement
. It can be append
(insert after the insertElement
), append_content
(insert as the last child of insertElement
), prepend
(insert before insertElement
), or prepend_content
(insert as the first child of insertElement
).
wrapPreviewInTex
: This is a boolean value that is 1 by default. If true and the display mode is HTML_MathJax, then the answer previews are wrapped in a math/tex type script tag.
showEntered
: This is a boolean value that is 1 by default. If true and the translator option showAttemptAnswers
is also true, then the student's evaluated (or "Entered") answer is shown in the feedback popover if the student has entered an answer.
showPreview
: This is a boolean value that is 1 by default. If true and the translator option showAttemptPreviews
is also true, then a preview of the student's answer is shown in the feedback popover. Most likely this should always be true, and most likely this option (and the translator option) shouldn't even exist!
showCorrect
: This is a boolean value that is 1 by default. If this is true and the translator option showCorrectAnswers
is nonzero, then a preview of the correct answer is shown in the feedback popover. In other words, this option prevents showing correct answers even if the frontend requests that correct answers be shown.
answerGiven
: This is a boolean value. This should be true if a student has answered a question, and false otherwise. By default this is set to 1 if the responses for all answers in the response group are non-empty, and 0 otherwise. For radio buttons and checkboxes this is if one of the inputs are checked or not. However, for some answers a non-empty response can occur even if a student has not answered a question (for example, this occurs for answers to questions created with the draggableProof.pl macro) . So macros that create answers with responses like that should override this.
manuallyGraded
: This is a boolean value. This should be true if the answer is not graded by the PG problem grader, but is graded manually at a later time, and should be false if the PG problem grader sets the grade for this answer. For example, essay answers created by the PGessaymacros.pl macro set this to true.
needsGrading
: This is a boolean value. This should be true if the answer is not graded by the PG problem grader, but is graded manually at a later time, and the answer has changed.
Essentially runs the pg problem specified by $filePath
, which is a path relative to the top of the templates directory. The output of that problem appears in the given problem.
includePGproblem($filePath);
These two subroutines can be used in filters to set default options. They help make filters perform in uniform, predictable ways, and also make it easy to recognize from the code which options a given filter expects.
Use this to assign aliases for the standard options. It must come before set_default_options within the subroutine.
assign_option_aliases(\%options,
alias1 => 'option5'
alias2 => 'option7'
);
If the subroutine is called with an option alias1 => 23
it will behave as if it had been called with the option option5 => 23
.
set_default_options(\%options,
_filter_name => 'filter',
option5 => .0001,
option7 => 'ascii',
allow_unknown_options => 0,
}
Note that the first entry is a reference to the options with which the filter was called.
The option5
is set to .0001 unless the option is explicitly set when the subroutine is called.
The _filter_name
option should always be set, although there is no error if it is missing. It is used mainly for debugging answer evaluators and allows you to keep track of which filter is currently processing the answer.
If allow_unknown_options
is set to 0 then if the filter is called with options which do NOT appear in the set_default_options list an error will be signaled and a warning message will be printed out. This provides error checking against misspelling an option and is generally what is desired for most filters.
Occasionally one wants to write a filter which accepts a long list of options, not all of which are known in advance, but only uses a subset of the options provided. In this case, setting allow_unkown_options
to 1 prevents the error from being signaled.