Skip to content

Commit e1cab4e

Browse files
committed
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).
1 parent e6ac294 commit e1cab4e

2 files changed

Lines changed: 236 additions & 0 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
## DESCRIPTION
2+
## Make a call to a Sage cell server from within a problem.
3+
## ENDDESCRIPTION
4+
5+
## DBsubject(WeBWorK)
6+
## DBchapter(WeBWorK tutorial)
7+
## DBsection(Problem Techniques)
8+
## Date(06/18/2026)
9+
## Institution(Missouri Western State University)
10+
## Author(Glenn Rice)
11+
## MO(1)
12+
## KEYWORDS('matrices', 'AskSage')
13+
14+
#:% name = AskSage
15+
#:% type = technique
16+
#:% categories = [matrices]
17+
18+
#:% section = preamble
19+
DOCUMENT();
20+
loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl');
21+
22+
#:% section = setup
23+
#: Determine a random number of rows, columns, and rank for the random matrix
24+
#: that will be generated by Sage.
25+
#:
26+
#: Send the code to generate the random matrix and its reduced row echelon form
27+
#: to the Sage cell service by calling the `AskSage` method. The first argument
28+
#: to the `AskSage` method must be the code for Sage to evaluate. The results
29+
#: are saved in keys of the `WEBWORK` dictionary. This is a special variable
30+
#: that the `AskSage` method looks for in the returned result and JSON decodes.
31+
#: Use this to save the data that is needed for the problem. The second optional
32+
#: argument is a reference to a hash of options. Generally, the only option is
33+
#: the `seed`. This can be used to pass the problem seed to Sage so that any
34+
#: random calls it makes also use that seed.
35+
#:
36+
#: After the `AskSage` method is returned check to see if the call failed with
37+
#: the `sageReturnedFail` method. If that is true, then the Sage cell server
38+
#: query failed. In that case set fallback values for the variables needed in
39+
#: the problem so that a workable problem is rendered in this case. If the call
40+
#: succeeded the the Sage `WEBWORK` dictionary will be decoded into the
41+
#: `webwork` hash key of the reply.
42+
#:
43+
#: Note that by default `AskSage` queries the public Sage cell service at
44+
#: https://sagecell.sagemath.org/service. However, that site no longer accepts
45+
#: public service requests. The service endpoint has been shut down due to
46+
#: security concerns. Thus the `AskSage` method will always fail with that URL.
47+
#: To fix this the WeBWorK administrator of your server can set up a local Sage
48+
#: cell service and configure `AskSage` to use that.
49+
Context('Matrix');
50+
51+
my $rows = random(3, 4);
52+
my $columns = random(4, 5);
53+
my $rank = random(2, 3);
54+
55+
$sageReply = AskSage(<<"END_CODE", { seed => $problemSeed });
56+
WEBWORK['M'] = random_matrix(
57+
QQ, $rows, $columns, algorithm = 'echelonizable', rank = $rank
58+
).rows()
59+
WEBWORK['RREF'] = matrix(QQ, WEBWORK['M']).rref().rows()
60+
END_CODE
61+
62+
if (sageReturnedFail($sageReply)) {
63+
# Fallback values for $M and $RREF in case the AskSage call fails.
64+
$M = Matrix([ [ -4, 8, -12, 15 ], [ 5, -10, 15, -19 ], [ 2, -4, 6, -5 ] ]);
65+
$RREF = Matrix([ [ 1, -2, 3, 0 ], [ 0, 0, 0, 1 ], [ 0, 0, 0, 0 ] ]);
66+
} else {
67+
$M = Matrix($sageReply->{webwork}{M});
68+
$RREF = Matrix($sageReply->{webwork}{RREF});
69+
}
70+
71+
#:% section = statement
72+
BEGIN_PGML
73+
Give the reduced row echelon form of the following matrix.
74+
75+
[```[$M]```]
76+
77+
The reduced row echelon form is
78+
79+
[_]*{$RREF}
80+
END_PGML
81+
82+
#:% section = solution
83+
BEGIN_PGML_SOLUTION
84+
Solution explanation goes here.
85+
END_PGML_SOLUTION
86+
87+
ENDDOCUMENT();
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
## DESCRIPTION
2+
## Add buttons to the MathQuill toolbar for a particular
3+
## answer using a named answer rule.
4+
## ENDDESCRIPTION
5+
6+
## DBsubject(WeBWorK)
7+
## DBchapter(WeBWorK tutorial)
8+
## DBsection(Problem Techniques)
9+
## Date(06/17/2026)
10+
## Institution(Missouri Western State University)
11+
## Author(Glenn Rice)
12+
## MO(1)
13+
## KEYWORDS('inequalities')
14+
15+
#:% name = Named answer rules
16+
#:% type = technique
17+
#:% categories = [equations, interval]
18+
19+
#:% section = preamble
20+
#: Load the PODLINK('parserLinearInequality.pl') macro so that a linear
21+
#: inequality answer can be used.
22+
DOCUMENT();
23+
loadMacros(
24+
'PGstandard.pl', 'PGML.pl',
25+
'parserLinearInequality.pl', 'PGcourse.pl'
26+
);
27+
28+
#:% section = setup
29+
#: Generate random variables `$a`, `$b`, and `$c` to use for coefficients in the
30+
#: linear inequality to be solved.
31+
#:
32+
#: Then define a linear inequality answer in the `LinearInequality` context,
33+
#: and an interval answer in the `Interval` context.
34+
$a = random(2, 9);
35+
$b = random(2, 9);
36+
$c = random(2, 9);
37+
38+
Context('LinearInequality');
39+
$inequalityAnswer = LinearInequality("x <= $a");
40+
41+
Context('Interval');
42+
$intervalAnswer = Interval("(-inf, $a]");
43+
44+
#:% section = statement
45+
#: Ask the student to solve the inequality, and give the answer as both a linear
46+
#: inequality and an interval.
47+
#:
48+
#: A named answer is needed for the linear inequality answer in order to add
49+
#: toolbar buttons to the MathQuill toolbar for that answer. Do this with the
50+
#: third option for a PGML answer by calling the `NEW_ANS_NAME` function and
51+
#: assigning that to the variable `$inequalityAnswerName`. Never use hard coded
52+
#: made up answer names in problems. Always obtain an answer name using the
53+
#: `NEW_ANS_NAME` method. Problems that use hard coded made up answer names will
54+
#: not work correctly in many situations. The interval answer does not need to
55+
#: be named.
56+
#:
57+
#: Finally inject a `script` tag to the end of the problem text. It is important
58+
#: that this JavaScript code be executed after the PG MathQuill JavaScript so
59+
#: that the `window.answerQuills` variable will be defined. One way to
60+
#: accomplish this is by executing the code in the `DOMContentLoaded` event.
61+
#:
62+
#: Add the buttons to the toolbar for the linear inequality answer by calling
63+
#: the `addToolbarButtons` function of the `options` object for the `mathField`
64+
#: associated to the `$inequalityAnswerName` answer quill. The first argument
65+
#: for the `addToolbarButtons` function can be either a single toolbar button
66+
#: definition or an array of toolbar button definitions (see below for a
67+
#: description of a toolbar button definition). The second argument can be
68+
#: either a number which is the zero based position at which the new toolbar
69+
#: buttons will be inserted, or the `id` of an existing toolbar button after
70+
#: which the new toolbar buttons will be inserted (note the `id`s of the default
71+
#: toolbar buttons in order are `frac`, `abs`, `sqrt`, `nthroot`, `exponent`,
72+
#: `infty`, `pi`, `vert`, `cup`, and `text`). Note that the second argument can
73+
#: be a negative number which counts backward from the end. In fact the second
74+
#: argument can be entirely omitted. In that case a position of -1 is used which
75+
#: will insert the new button before the last toolbar button (initially the text
76+
#: button).
77+
#:
78+
#: A toolbar button is defined as a JavaScript object with the four properties
79+
#: `id`, `latex`, `tooltip`, and `icon`.
80+
#:
81+
#: - The `id` can be any string, but should not have the same `id` of another
82+
#: toolbar button.
83+
#: - The `latex` for a button is the LaTeX code that will be inserted into the
84+
#: MathQuill input when the toolbar button is pressed. Make sure that
85+
#: backslashes are escaped with another backslash.
86+
#: - The `tooltip` is text that will be shown in a tooltip when the button on
87+
#: the toolbar has keyboard focus or is hovered over by the mouse cursor. Note
88+
#: that the toolbar text should include a brief textual description of what
89+
#: will be inserted when the button is pressed, and in parentheses after that
90+
#: the keyboard sequence that can be typed into the MathQuill input to insert
91+
#: the same thing that pressing the button will insert.
92+
#: - The `icon` is the LaTeX code for icon that will be shown in a static
93+
#: MathQuill field on the toolbar button. Make sure that backslashes are
94+
#: escaped with another backslash.
95+
#:
96+
#: A button can also be removed from the toolbar for an answer using the
97+
#: `removeToolbarButtons` function. The only argument for this function is a
98+
#: string `id` or array of string `id`s for existing toolbar buttons to be
99+
#: removed. For example,
100+
#: `answerQuills.$inequalityAnswerName.mathField.options.removeToolbarButtons('cup');`
101+
#: would remove the union button from the toolbar, and
102+
#: `answerQuills.$inequalityAnswerName.mathField.options.removeToolbarButtons([ 'pi', 'cup' ]);`
103+
#: would remove the pi and union buttons from the toolbar. It is important to
104+
#: note that removing a toolbar button does not prevent a student from entering
105+
#: what the toolbar button would insert. It merely removes the toolbar button.
106+
#: For example, if the `cup` button is removed, a student could still type
107+
#: "union" to enter a union symbol.
108+
BEGIN_PGML
109+
Solve the following inequality.
110+
111+
>>[``[$b]x + [$c] \leq [$a * $b + $c]``]<<
112+
113+
Express the answer as an inequality:
114+
[_]{$inequalityAnswer}{15}{ $inequalityAnswerName = NEW_ANS_NAME() }.
115+
116+
Express the answer in interval notation: [_]{$intervalAnswer}{15}
117+
END_PGML
118+
119+
TEXT(MODES(TeX => '', HTML => <<"END_SCRIPT"));
120+
<script>
121+
window.addEventListener('DOMContentLoaded', () => {
122+
if (!window.answerQuills) return;
123+
answerQuills.$inequalityAnswerName.mathField.options.addToolbarButtons(
124+
[
125+
{
126+
id: 'leq',
127+
latex: '\\leq',
128+
tooltip: 'less than or equal (<=)',
129+
icon: '\\leq'
130+
},
131+
{
132+
id: 'geq',
133+
latex: '\\geq',
134+
tooltip: 'greater than or equal (>=)',
135+
icon: '\\geq'
136+
}
137+
],
138+
9
139+
);
140+
});
141+
</script>
142+
END_SCRIPT
143+
144+
#:% section = solution
145+
BEGIN_PGML_SOLUTION
146+
Solution explanation goes here.
147+
END_PGML_SOLUTION
148+
149+
ENDDOCUMENT();

0 commit comments

Comments
 (0)