Skip to content

Commit

Permalink
Merge pull request #10 from jverzani/issue_9
Browse files Browse the repository at this point in the history
add matchq question type
  • Loading branch information
jverzani committed Apr 6, 2022
2 parents 27b35af + 9e8f2a7 commit 9764458
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 2 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.2.1"
version = "0.3.0"

[deps]
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
Expand Down
18 changes: 18 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,24 @@ Who was the first president?
stringq(r"^Washington", label="last name")
```

* Here is an example of matching

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)
```

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)
```

----

Expand All @@ -88,4 +105,5 @@ radioq
yesnoq
booleanq
multiq
matchq
```
2 changes: 1 addition & 1 deletion src/QuizQuestions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ include("question_types.jl")
include("html_templates.jl")
include("show_methods.jl")

export numericq, radioq, multiq, booleanq, yesnoq, stringq
export numericq, radioq, multiq, booleanq, yesnoq, stringq, matchq

end
40 changes: 40 additions & 0 deletions src/html_templates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,43 @@ rb.addEventListener("change", function() {
$(grading_partial)
})});
"""

## ----

html_templates["Matchq"] = mt"""
<table>
{{#:ITEMS}}
<tr>
<td>
<label for "select_{{:ID}}">{{{:QUESTION}}}</label>
</td>
<td>
<select name = "select_{{:ID}}" id="select_{{:ID}}_{{:NO}}">
<option value=0 selected="selected">{{{:BLANK}}}</option>
{{#:ANSWER_CHOICES}}
<option value="{{:INDEX}}">{{{:LABEL}}}</option>
{{/:ANSWER_CHOICES}}
</select>
</td>
<tr>
{{/:ITEMS}}
</table>
"""

html_templates["matchq_grading_script"] = """
function callback(element, iterator) {
element.addEventListener("change", function() {
var a = [];
var selectors = document.querySelectorAll('[id ^= "select_{{:ID}}"]');
Array.prototype.forEach.call(selectors, function (element, iterator) {
a.push(element.value);
})
b = {{{:CORRECT_ANSWER}}};
// https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript
correct = (a.length === b.length && a.find((v,i) => v !== b[i]) === undefined)
var msgBox = document.getElementById('{{:ID}}_message');
$(grading_partial)
})
}
Array.prototype.forEach.call(document.querySelectorAll('[id ^= "select_{{:ID}}"]'), callback);
"""
62 changes: 62 additions & 0 deletions src/question_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,68 @@ function multiq(choices, answers;
values, labels[inds], label, hint, inline)
end

##
mutable struct Matchq <: Question
questions
choices
answer
label
hint
end

"""
matchq(questions, choices, answers; label="", hint="")
matchq(d::Dictionary; label="", hint="")
Use a drop down to select the right match for each question.
Arguments:
* `questions`: Indexable collection of questions.
* `choices`: indexable collection of choices for each question. As seen in the example, choices can be formatted with markdown.
* `answers`: collection of correct indices for each question.
* `d`: As an alternative, a dictionary of questions and answers can be specified. The choices will be taken from the values then randomized, the answers will be computed.
* `label`: optional label for the form element
* `hint`: optional plain-text hint that can be seen on hover
## Examples:
```
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)
d = Dict("Select a Volvo" => "XC90", "Select a Mercedes" => "GLE 250")
matchq(d)
```
"""
function matchq(questions, choices, answers;
label="", hint="")

@assert length(questions) == length(answers)

Matchq(questions, choices, answers,
label, hint)
end

function matchq(d::AbstractDict; kwargs...)

questions = collect(keys(d))
choices = collect(values(d))
n = length(questions)
inds = shuffle(1:n)

matchq(questions, choices[inds], sortperm(inds); kwargs...)
end



"""
booleanq(ans; [label, hint])
Expand Down
30 changes: 30 additions & 0 deletions src/show_methods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,33 @@ function Base.show(io::IO, m::MIME"text/html", x::Multiq)
)

end


function Base.show(io::IO, m::MIME"text/html", x::Matchq)

ID = randstring()
BLANK = "Choose..."
ANSWER_CHOICES = [(INDEX=i, LABEL=_markdown_to_html(label)) for (i, label) in enumerate(x.choices)]
items = [(ID=ID, NO=i, BLANK=BLANK, QUESTION=_markdown_to_html(question), ANSWER_CHOICES=ANSWER_CHOICES) for (i,question) enumerate(x.questions)]
GRADING_SCRIPT = Mustache.render(html_templates["matchq_grading_script"];
ID = ID,
CORRECT_ANSWER = collect(string.(x.answer)),
INCORRECT = "Not yet",
CORRECT = "Correct"
)
FORM = Mustache.render(html_templates["Matchq"];
ID = ID,
ITEMS = items,

)

Mustache.render(io, html_templates["question_tpl"],
ID = ID,
TYPE = "radio",
FORM = FORM,
GRADING_SCRIPT = GRADING_SCRIPT,
LABEL=_markdown_to_html(x.label),
HINT = x.hint # use HINT in question
)

end
7 changes: 7 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@ using Test
@test radioq(1:3, 1, keep_order=true).answer == 1

@test multiq(("one","two","three"), (2, 3), label="Primes?").label == "Primes?"

d = Dict("Select a Volvo" => "XC90", "Select a Mercedes" => "GLE 350",
"Select an Audi" => "A4")
r = matchq(d)
i = rand(1:3)
@test d[r.questions[i]] == r.choices[r.answer[i]]

end

2 comments on commit 9764458

@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/58025

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.0 -m "<description of version>" 97644589e982c6f4d0d36ae0ced546c3603deffe
git push origin v0.3.0

Please sign in to comment.