From 2a1dab7754d3784320024a0919a2ece1ec5a2619 Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Thu, 18 Jun 2026 20:39:47 -0500 Subject: [PATCH] Add two more problem technique sample problems. These are both problems from https://wiki.openwebwork.org/wiki/Problem_Techniques that demonstrate valid and current techniques in problems that were not added before for some reason. Add a `NamedAnswerRules.pg` problem that demonstrates how to use named answer rules to add buttons to the MathQuill toolbar for the named rule. This is an updated version of https://wiki.openwebwork.org/wiki/NamedAnswerRules. With PG 2.21 the way to do so is changing. So this is needed to demonstrate the new way to do it. Add an `AskSage.pl` problem that demonstrates how to use the `AskSage` method to send a request to a Sage cell server. This is an updated version of https://wiki.openwebwork.org/wiki/AskSage. Note that this version of the problem depends on the changes in #1399. Also note that without #1399 there is no hope for the `AskSage` method in general (so lets get that merged). --- .../ProblemTechniques/AskSage.pg | 87 ++++++++++ .../ProblemTechniques/NamedAnswerRules.pg | 149 ++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 tutorial/sample-problems/ProblemTechniques/AskSage.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/NamedAnswerRules.pg diff --git a/tutorial/sample-problems/ProblemTechniques/AskSage.pg b/tutorial/sample-problems/ProblemTechniques/AskSage.pg new file mode 100644 index 000000000..458770199 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/AskSage.pg @@ -0,0 +1,87 @@ +## DESCRIPTION +## Make a call to a Sage cell server from within a problem. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/18/2026) +## Institution(Missouri Western State University) +## Author(Glenn Rice) +## MO(1) +## KEYWORDS('matrices', 'AskSage') + +#:% name = AskSage +#:% type = technique +#:% categories = [matrices] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: Determine a random number of rows, columns, and rank for the random matrix +#: that will be generated by Sage. +#: +#: Send the code to generate the random matrix and its reduced row echelon form +#: to the Sage cell service by calling the `AskSage` method. The first argument +#: to the `AskSage` method must be the code for Sage to evaluate. The results +#: are saved in keys of the `WEBWORK` dictionary. This is a special variable +#: that the `AskSage` method looks for in the returned result and JSON decodes. +#: Use this to save the data that is needed for the problem. The second optional +#: argument is a reference to a hash of options. Generally, the only option is +#: the `seed`. This can be used to pass the problem seed to Sage so that any +#: random calls it makes also use that seed. +#: +#: After the `AskSage` method is returned check to see if the call failed with +#: the `sageReturnedFail` method. If that is true, then the Sage cell server +#: query failed. In that case set fallback values for the variables needed in +#: the problem so that a workable problem is rendered in this case. If the call +#: succeeded the the Sage `WEBWORK` dictionary will be decoded into the +#: `webwork` hash key of the reply. +#: +#: Note that by default `AskSage` queries the public Sage cell service at +#: https://sagecell.sagemath.org/service. However, that site no longer accepts +#: public service requests. The service endpoint has been shut down due to +#: security concerns. Thus the `AskSage` method will always fail with that URL. +#: To fix this the WeBWorK administrator of your server can set up a local Sage +#: cell service and configure `AskSage` to use that. +Context('Matrix'); + +my $rows = random(3, 4); +my $columns = random(4, 5); +my $rank = random(2, 3); + +$sageReply = AskSage(<<"END_CODE", { seed => $problemSeed }); +WEBWORK['M'] = random_matrix( + QQ, $rows, $columns, algorithm = 'echelonizable', rank = $rank +).rows() +WEBWORK['RREF'] = matrix(QQ, WEBWORK['M']).rref().rows() +END_CODE + +if (sageReturnedFail($sageReply)) { + # Fallback values for $M and $RREF in case the AskSage call fails. + $M = Matrix([ [ -4, 8, -12, 15 ], [ 5, -10, 15, -19 ], [ 2, -4, 6, -5 ] ]); + $RREF = Matrix([ [ 1, -2, 3, 0 ], [ 0, 0, 0, 1 ], [ 0, 0, 0, 0 ] ]); +} else { + $M = Matrix($sageReply->{webwork}{M}); + $RREF = Matrix($sageReply->{webwork}{RREF}); +} + +#:% section = statement +BEGIN_PGML +Give the reduced row echelon form of the following matrix. + +[```[$M]```] + +The reduced row echelon form is + +[_]*{$RREF} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/NamedAnswerRules.pg b/tutorial/sample-problems/ProblemTechniques/NamedAnswerRules.pg new file mode 100644 index 000000000..16b56f8df --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/NamedAnswerRules.pg @@ -0,0 +1,149 @@ +## DESCRIPTION +## Add buttons to the MathQuill toolbar for a particular +## answer using a named answer rule. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/17/2026) +## Institution(Missouri Western State University) +## Author(Glenn Rice) +## MO(1) +## KEYWORDS('inequalities') + +#:% name = Named answer rules +#:% type = technique +#:% categories = [equations, interval] + +#:% section = preamble +#: Load the PODLINK('parserLinearInequality.pl') macro so that a linear +#: inequality answer can be used. +DOCUMENT(); +loadMacros( + 'PGstandard.pl', 'PGML.pl', + 'parserLinearInequality.pl', 'PGcourse.pl' +); + +#:% section = setup +#: Generate random variables `$a`, `$b`, and `$c` to use for coefficients in the +#: linear inequality to be solved. +#: +#: Then define a linear inequality answer in the `LinearInequality` context, +#: and an interval answer in the `Interval` context. +$a = random(2, 9); +$b = random(2, 9); +$c = random(2, 9); + +Context('LinearInequality'); +$inequalityAnswer = LinearInequality("x <= $a"); + +Context('Interval'); +$intervalAnswer = Interval("(-inf, $a]"); + +#:% section = statement +#: Ask the student to solve the inequality, and give the answer as both a linear +#: inequality and an interval. +#: +#: A named answer is needed for the linear inequality answer in order to add +#: toolbar buttons to the MathQuill toolbar for that answer. Do this with the +#: third option for a PGML answer by calling the `NEW_ANS_NAME` function and +#: assigning that to the variable `$inequalityAnswerName`. Never use hard coded +#: made up answer names in problems. Always obtain an answer name using the +#: `NEW_ANS_NAME` method. Problems that use hard coded made up answer names will +#: not work correctly in many situations. The interval answer does not need to +#: be named. +#: +#: Finally inject a `script` tag to the end of the problem text. It is important +#: that this JavaScript code be executed after the PG MathQuill JavaScript so +#: that the `window.answerQuills` variable will be defined. One way to +#: accomplish this is by executing the code in the `DOMContentLoaded` event. +#: +#: Add the buttons to the toolbar for the linear inequality answer by calling +#: the `addToolbarButtons` function of the `options` object for the `mathField` +#: associated to the `$inequalityAnswerName` answer quill. The first argument +#: for the `addToolbarButtons` function can be either a single toolbar button +#: definition or an array of toolbar button definitions (see below for a +#: description of a toolbar button definition). The second argument can be +#: either a number which is the zero based position at which the new toolbar +#: buttons will be inserted, or the `id` of an existing toolbar button after +#: which the new toolbar buttons will be inserted (note the `id`s of the default +#: toolbar buttons in order are `frac`, `abs`, `sqrt`, `nthroot`, `exponent`, +#: `infty`, `pi`, `vert`, `cup`, and `text`). Note that the second argument can +#: be a negative number which counts backward from the end. In fact the second +#: argument can be entirely omitted. In that case a position of -1 is used which +#: will insert the new button before the last toolbar button (initially the text +#: button). +#: +#: A toolbar button is defined as a JavaScript object with the four properties +#: `id`, `latex`, `tooltip`, and `icon`. +#: +#: - The `id` can be any string, but should not have the same `id` of another +#: toolbar button. +#: - The `latex` for a button is the LaTeX code that will be inserted into the +#: MathQuill input when the toolbar button is pressed. Make sure that +#: backslashes are escaped with another backslash. +#: - The `tooltip` is text that will be shown in a tooltip when the button on +#: the toolbar has keyboard focus or is hovered over by the mouse cursor. Note +#: that the toolbar text should include a brief textual description of what +#: will be inserted when the button is pressed, and in parentheses after that +#: the keyboard sequence that can be typed into the MathQuill input to insert +#: the same thing that pressing the button will insert. +#: - The `icon` is the LaTeX code for icon that will be shown in a static +#: MathQuill field on the toolbar button. Make sure that backslashes are +#: escaped with another backslash. +#: +#: A button can also be removed from the toolbar for an answer using the +#: `removeToolbarButtons` function. The only argument for this function is a +#: string `id` or array of string `id`s for existing toolbar buttons to be +#: removed. For example, +#: `answerQuills.$inequalityAnswerName.mathField.options.removeToolbarButtons('cup');` +#: would remove the union button from the toolbar, and +#: `answerQuills.$inequalityAnswerName.mathField.options.removeToolbarButtons([ 'pi', 'cup' ]);` +#: would remove the pi and union buttons from the toolbar. It is important to +#: note that removing a toolbar button does not prevent a student from entering +#: what the toolbar button would insert. It merely removes the toolbar button. +#: For example, if the `cup` button is removed, a student could still type +#: "union" to enter a union symbol. +BEGIN_PGML +Solve the following inequality. + +>>[``[$b]x + [$c] \leq [$a * $b + $c]``]<< + +Express the answer as an inequality: +[_]{$inequalityAnswer}{15}{ $inequalityAnswerName = NEW_ANS_NAME() }. + +Express the answer in interval notation: [_]{$intervalAnswer}{15} +END_PGML + +TEXT(MODES(TeX => '', HTML => <<"END_SCRIPT")); + +END_SCRIPT + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT();