Skip to content

Commit

Permalink
Merge pull request #23 from jverzani/fill_blank
Browse files Browse the repository at this point in the history
fill in the blank questions; edit documentation
  • Loading branch information
jverzani committed Apr 15, 2022
2 parents 7d6636e + aa5a751 commit 1cc5f47
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 41 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "QuizQuestions"
uuid = "612c44de-1021-4a21-84fb-7261cf5eb2d4"
authors = ["jverzani <jverzani@gmail.com> and contributors"]
version = "0.3.6"
version = "0.3.7"

[deps]
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
Expand Down
122 changes: 91 additions & 31 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[QuizQuestions](https://github.com/jverzani/QuizQuestions.jl) allows the inclusion of self-grading quiz questions within a `Documenter`, `Weave`, or `Pluto` HTML page.

## Basics

The basic idea is:

Expand All @@ -11,7 +12,7 @@ The basic idea is:
using QuizQuestions
```

* create a question:
* create a question with `Julia` code:

```@example quiz_question
choices = ["one", "``2``", raw"``\sqrt{9}``"]
Expand All @@ -22,50 +23,59 @@ radioq(choices, answer; label=question, hint="A hint")

* repeat as desired.


The quizzes are written in markdown with the questions in `Julia`
blocks. The above code cells would be enclosed in triple-backtick
blocks and would typically have their contents hidden from the user. How this is
done varies between `Documenter`, `Weave`, and `Pluto`. The `examples`
directory shows examples of each.

----

The `show` method of the object for the `text/html` mime type produced by `radioq` inserts the necessary HTML and JavaScript code to show the input widget and grading logic.
For each question:

* The `show` method of the object for the `text/html` mime type
inserts the necessary HTML and JavaScript code to show the input
widget and grading logic.

* the optional `hint` argument allows a text-only hint available to
the user on hover.

The above question uses radio buttons for allowing one choice from many.
* The optional `label` argument is used to flag the question.

The `hint` argument allows an optional text-only hint available to the user on hover. The `label` is used to flag the question. This is also optional. For example, the question can be asked in the body of the document (the position of any hint will be different):
For example, the question can be asked in the body of the document
(the position of any hint will be different):

What is ``\sqrt{2}``?

```@example quiz_question
answer = sqrt(2)
tol = 1e-3
numericq(answer, tol, hint="you need to be within 1/1000")
numericq(answer, tol,
label=raw"What is ``\sqrt{2}``?",
hint="you need to be within 1/1000")
```


The quiz questions are written in markdown, as would be the rest of the Documenter or Weave document containing the questions. The above code cells would be enclosed in triple-backtick blocks and would have their contents hidden from the user. How this is done varies from `Documenter`, `Weave`, and `Pluto`. The `examples` directory shows examples of each.
## Examples of question types

### Choice questions (one from many)

* Here is the `buttonq` alternative to radio buttons:

Which adds to ``2``?

```@example quiz_question
buttonq(["``1 + 1``", "``2+2``", "``-1 + -1``"], 1;
explanation="Add 'em up")
```

The `radioq` question was shown above.

The `buttonq` question is alternative to radio buttons where the correct answer is shown after the first choice. If this choice is wrong, the explanation is shown along with the correct answer.

* Here is an example of a numeric question:

```@example quiz_question
answer = 1 + 1
numericq(answer; label="``1 + 1``?", hint="Do the math")
buttonq(["``1 + 1``", "``2+2``", "``-1 + -1``"], 1;
label = "Which adds to ``2``?",
explanation="Add 'em up")
```

Numeric questions can have an absolute tolerance set to allow for rounding.
### Multiple choices (one or more from many)

* Here is an example of a multiple choice question (one or *more* from many):

Select the sentences with numbers:
A multiple choice question (one or *more* from many) can be constructed with `multieq`:

```@example quiz_question
choices =["Four score and seven years ago",
Expand All @@ -74,48 +84,98 @@ choices =["Four score and seven years ago",
"One and one and one makes three"
]
ans = (1, 4)
multiq(choices, ans, label="Select one or more")
multiq(choices, ans,
label="Select the sentences with numbers (one or more)")
```

* Here is an example of a short text question graded by a regular expression:

### Numeric answers

Who was the first president?
Questions with numeric answers use `numericq`. The question is graded when the input widget loses focus.

```@example quiz_question
stringq(r"^Washington", label="last name")
answer = 1 + 1
numericq(answer;
label="``1 + 1``?",
hint="Do the math")
```

* Here is an example of matching
Numeric questions can have an absolute tolerance set to allow for rounding.


### Text response

A question graded by a regular expression can be asked with `stringq`. The question is graded when the input widget loses focus.



```@example quiz_question
stringq(r"^Washington"; label="Who was the first president?",
placeholder="last name")
```

### Matching

A question involving matching can be asked with `matchq`.

For each question, select the correct answer.

```@example quiz_question
questions = ("Select a Volvo", "Select a Mercedes", "Select an Audi")
choices = ("XC90", "A4", "GLE 350", "X1") # may be more than questions
answer = (1,3,2) # indices of correct
matchq(questions, choices, answer)
matchq(questions, choices, answer;
label="For each question, select the correct answer.")
```

The above shows that the number of choices need not match the number of questions. When they do, a dictionary can be used to specify the choices and the answers will be computed:

```@example quiz_question
d = Dict("Select a Volvo" => "XC90", "Select a Mercedes" => "GLE 350",
"Select an Audi" => "A4")
matchq(d)
matchq(d, label="Match the manufacture with a model")
```

### Fill-in-the-blank questions

Fill-in-the blank questions can be asked with `fillblankq`. Answers can be gathered and graded in different manners.


```@example quiz_question
question = "The quick brown fox jumped over the ____ dog"
fillblankq(question, ("lazy", "brown", "sleeping"), 1)
```

----

(like `stringq`)

```@example quiz_question
question = "The quick brown fox jumped over the ____ dog"
fillblankq(question, r"lazy")
```

----

(like `numericq`)

```@example quiz_question
question = "____ ``+ 2 = 4``"
fillblankq(question, 2)
```


## Reference

Currently only a few question types are available:

```@docs
numericq
stringq
radioq
buttonq
yesnoq
booleanq
multiq
matchq
numericq
stringq
fillblankq
```
2 changes: 1 addition & 1 deletion src/QuizQuestions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ include("show_methods.jl")
export numericq,
buttonq, radioq, booleanq, yesnoq,
multiq, matchq,
stringq
stringq, fillblankq

end
18 changes: 17 additions & 1 deletion src/html_templates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ html_templates["Matchq"] = mt"""
<label for "select_{{:ID}}">{{{:QUESTION}}}</label>
</td>
<td>
<select name = "select_{{:ID}}" id="select_{{:ID}}_{{:NO}}">
<select name = "select_{{:ID}}" id="select_{{:ID}}">
<option value=0 selected="selected">{{{:BLANK}}}</option>
{{#:ANSWER_CHOICES}}
<option value="{{:INDEX}}">{{{:LABEL}}}</option>
Expand Down Expand Up @@ -195,3 +195,19 @@ html_templates["matchq_grading_script"] = """
}
Array.prototype.forEach.call(document.querySelectorAll('[id ^= "select_{{:ID}}"]'), callback);
"""


## ----

html_templates["fill_in_blank_select"] = """
<select name = "select_{{:ID}}" id="select_{{:ID}}">
<option value=0 selected="selected">{{{:BLANK}}}</option>
{{#:ANSWER_CHOICES}}
<option value="{{:INDEX}}">{{{:LABEL}}}</option>
{{/:ANSWER_CHOICES}}
</select>
"""

html_templates["fill_in_blank_input"] = """
<input id="{{:ID}}" type="{{:TYPE}}" class="form-control" placeholder="{{:PLACEHOLDER}}">
"""
83 changes: 78 additions & 5 deletions src/question_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mutable struct Stringq <: Question
re::Regex
label
hint
placeholder
end

"""
Expand All @@ -16,9 +17,9 @@ Arguments:
* `re`: a regular expression for grading
* `label`: optional label for the form element
* `hint`: optional plain-text hint that can be seen on hover
* `placeholder`: text shown when input widget is initially drawn
"""
stringq(re::Regex; label="", hint="") = Stringq(re, label, hint)
stringq(re::Regex; label="", hint="", placeholder=nothing) = Stringq(re, label, hint, placeholder)


##
Expand All @@ -28,10 +29,11 @@ mutable struct Numericq <: Question
units
label
hint
placeholder
end

"""
numericq(value, tol=1e-3; label="", hint="", units="")
numericq(value, tol=1e-3; label="", hint="", units="", placeholder=nothing)
Match a numeric answer
Expand All @@ -42,13 +44,15 @@ Arguments:
* `label`: optional label for the form element
* `hint`: optional plain-text hint that can be seen on hover
* `units`: a string indicating expected units.
* `placeholder`: text shown when input widget is initially drawn
"""
function numericq(val, tol=1e-3, args...;
label="",
hint="", units::AbstractString="")
hint="", units::AbstractString="",
placeholder=nothing)

Numericq(val, tol, units, label, hint)
Numericq(val, tol, units, label, hint, placeholder)
end

numericq(val::Int; kwargs...) = numericq(val, 0; kwargs...)
Expand Down Expand Up @@ -305,3 +309,72 @@ yesnoq(true)
"""
yesnoq(ans::AbstractString, args...; kwargs...) = radioq(["Yes", "No"], ans == "yes" ? 1 : 2, args...; keep_order=true, kwargs...)
yesnoq(ans::Bool, args...; kwargs...) = yesnoq(ans ? "yes" : "no", args...;kwargs...)

## --------

abstract type FillBlankQ <: Question end

mutable struct FillBlankChoiceQ <: FillBlankQ
question
choices
answer
label
hint
end

mutable struct FillBlankStringQ <: FillBlankQ
question
re::Regex
label
hint
placeholder
end

mutable struct FillBlankNumericQ <: FillBlankQ
question
val
tol
label
hint
placeholder
end


"""
fillblankq(question answer::Regex; label="", hint="", placeholder=nothing)
fillblankq(question, choices, answer; label="", hint="", keep_order=false)
fillblankq(question, val, tol=0; label="", hint="", placeholder=nothing)
Present a fill-in-the-blank question where the blank can be a selection, a number, or a string graded by a regular expression.
* `question`: A string. Use `____` (4 or more under scores) to indicate the blank
## Examples
```
question = "The quick brown fox jumped over the ____ dog"
fillblankq(question, r"lazy")
fillblankq(question, ("lazy", "brown", "sleeping"), 1)
fillblankq("____ ``+ 2 = 4``", 2)
```
"""
fillblankq(question, answer::Regex; label="", hint="", placeholder=nothing) =
FillBlankStringQ(question, answer, label, hint, placeholder)


function fillblankq(question, choices, answer; label="", hint="", keep_order=false)

if !keep_order
inds = collect(1:length(choices))
values = copy(inds)
labels = choices
!keep_order && shuffle!(inds)
choices = choices[inds]
answer = findfirst(isequal(answer), inds)
end


FillBlankChoiceQ(question, choices, answer, label, hint)
end

fillblankq(question, val::Real, tol=0; label="", hint="", placeholder=nothing) =
FillBlankNumericQ(question, val, tol, label, hint, placeholder)
Loading

2 comments on commit 1cc5f47

@jverzani
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/58594

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.3.7 -m "<description of version>" 1cc5f47d86cc1c2f1e03bcf77145525f77d94a0c
git push origin v0.3.7

Please sign in to comment.