Include Figure

Adding static or dynamic figures to questions

Simple example

This is an example showing how we can add an image to a question

where we only need to write the question.html file:

<pl-question-panel>
<p>
The function $f(x) = -x^3 - x^2 - 10x - 9$ is illustrated below. Select the values of $x$
corresponding to positive function values.
</p>
<pl-figure file-name=static_image.png directory='clientFilesQuestion'></pl-figure>
</pl-question-panel>
<pl-checkbox answers-name="select" hide-letter-keys="true" >
<pl-answer correct="true"> $x = -6$ </pl-answer>
<pl-answer correct="true"> $x = -2$ </pl-answer>
<pl-answer correct="false"> $x = 0$ </pl-answer>
<pl-answer correct="false"> $x = 4$ </pl-answer>
</pl-checkbox>

The plot is added from a static image static_image.png using the element pl-figure. We use pl-checkbox to add the possible answers that can be selected by the user, including the correct answers and distractors.

Complex example

The same example can be generated using different levels of randomization, including:

  • Randomized function f(x)f(x)
  • Selected correct answers can be positive or negative numbers

The modified question.html file that supports the randomization is:

<pl-question-panel>
<p>
The function $f(x) = {{params.f}}$ is illustrated below. Select the values of $x$
corresponding to {{params.option}} function values.
</p>
<pl-figure file-name=figure.png type="dynamic"></pl-figure>
</pl-question-panel>
<pl-checkbox answers-name="select" hide-letter-keys="true" number-answers=4>
<pl-answer correct={{params.x0}}> $x = -6$ </pl-answer>
<pl-answer correct={{params.x1}}> $x = -4$ </pl-answer>
<pl-answer correct={{params.x2}}> $x = -2$ </pl-answer>
<pl-answer correct={{params.x3}}> $x = 0$ </pl-answer>
<pl-answer correct={{params.x4}}> $x = 2$ </pl-answer>
<pl-answer correct={{params.x5}}> $x = 4$ </pl-answer>
<pl-answer correct={{params.x6}}> $x = 6$ </pl-answer>
</pl-checkbox>

To generate the parameters, we can use the following Python code in server.py:

import matplotlib.pyplot as plt
import io
import random
import numpy as np
import sympy as sym
import matplotlib as ml
ml.rcParams['text.usetex'] = True
def func(x,a,b,c):
return a*x**3 + b*x**2 + c*x - 9
def generate(data):
# generating the coefficients for the function
a = random.choice([-1,0,1])
b = random.choice([-1,1])
c = random.choice([10,-10])
data['params']['a'] = a
data['params']['b'] = b
data['params']['c'] = c
# Generate the function for display
x = sym.symbols('x')
data['params']['f'] = sym.latex(a*x**3 + b*x**2 + c*x - 9)
# Generate x and y values for the checkbox options
xp = np.array([-6,-4,-2,0,2,4,6])
yp = func(xp,a,b,c)
# Generate question parameter
option = np.random.choice(["positive", "negative"])
data['params']['option'] = option
# Determine the true and false options
ysol = yp>0 if option=="positive" else yp<0
solutions = ["true" if b else "false" for b in ysol]
# Storing the correct answers in the data dictionary
for i,s in enumerate(solutions):
varName = "x" + str(i)
data['params'][varName] = s
def file(data):
if data['filename']=='figure.png':
# Generate data points for the plot
xp = np.linspace(-6, 6, num=60)
a = data['params']['a']
b = data['params']['b']
c = data['params']['c']
yp = func(xp,a,b,c)
# Generate the plot
fig, ax = plt.subplots()
ax.plot(xp, yp)
plt.xlabel(r"$x$")
plt.ylabel(r"$f(x)$")
plt.grid()
plt.xlim(-6,6)
# Save the figure and return it as a buffer
buf = io.BytesIO()
plt.savefig(buf, format='png')
return buf

The above script randomizes and computes several aspects of the question:

1) Randomized function f(x)f(x):

The coefficients a,b,ca, b, c are selected from a list of possible coefficients to ensure the function will satisfy some pre-determined requirements. We use the Python library Sympy to create the symbolic expression for f(x)=ax3+bx2+cx9f(x) = a x^3 + b x^2 + c x - 9 and store this expression as the variable f in the data['params'] dictionary.

2) Randomized expected sign for correct answers

The correct answers can correspond to either negative or positive function values. This option is stored as variable option in the data["params"] dictionary.

3) Dynamic plot for the randomized function

In pl-figure with type="dynamic" the contents of the image file are returned by the function file() located in server.py (since the plot depends on the choice of the function coefficients). In this example, the code in file() generates the "fake" image figure.png

4) Checkbox answers matching the randomized parameters

The correct answers depend on the variables f and option. The boolean values for each answer (e.g. x=6x = -6) are stored as parameters in the data dictionary (e.g params.x0) in the server.py file. We use the attribute number-answers=4 so that only 4 out of the 7 possible answers are displayed.

Here's one instance of this fully randomized question: