diff --git a/Project.toml b/Project.toml index ea80d6a..e3b5085 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "QuizQuestions" uuid = "612c44de-1021-4a21-84fb-7261cf5eb2d4" authors = ["jverzani and contributors"] -version = "0.2.0" +version = "0.3.0" [deps] Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" diff --git a/src/QuizQuestions.jl b/src/QuizQuestions.jl index 33937db..4c21483 100644 --- a/src/QuizQuestions.jl +++ b/src/QuizQuestions.jl @@ -8,6 +8,6 @@ include("question_types.jl") include("html_templates.jl") include("show_methods.jl") -export numericq, radioq, booleanq, yesnoq, stringq +export numericq, radioq, multiq, booleanq, yesnoq, stringq end diff --git a/src/html_templates.jl b/src/html_templates.jl index ebad5dc..0175698 100644 --- a/src/html_templates.jl +++ b/src/html_templates.jl @@ -1,11 +1,11 @@ ## Could tidy up this HTML to make it look nicer html_templates = Dict() -const grading_partial = """ +grading_partial = """ if(correct) { - msgBox.innerHTML = "
👍 Correct
"; + msgBox.innerHTML = "
👍  {{#:CORRECT}}{{{:CORRECT}}}{{/:CORRECT}}{{^:CORRECT}}Correct{{/:CORRECT}}
"; } else { - msgBox.innerHTML = "
👎  Incorrect
"; + msgBox.innerHTML = "
👎  {{#:INCORRECT}}{{{:INCORRECT}}}{{/:INCORRECT}}{{^:INCORRECT}}Incorrect{{/:INCORRECT}}
"; } """ @@ -82,3 +82,37 @@ rb.addEventListener("change", function() { $(grading_partial) })}); """ + +html_templates["Multiq"] = mt""" +{{#:ITEMS}} +
+ +
+{{/:ITEMS}} +""" + +html_templates["multi_grading_script"] = """ +document.querySelectorAll('input[name="check_{{:ID}}"]').forEach(function(rb) { +rb.addEventListener("change", function() { + var choice_buttons = document.getElementsByName("check_{{:ID}}"); + selected = []; + for (var i=0; i < choice_buttons.length; i++) { + if (choice_buttons[i].checked) { + selected.push(i+1) + } + } + var a = selected; + 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) +})}); +""" diff --git a/src/question_types.jl b/src/question_types.jl index 489a0bb..e9e18f3 100644 --- a/src/question_types.jl +++ b/src/question_types.jl @@ -107,6 +107,60 @@ function radioq(choices, answer::Integer; end + + +## +mutable struct Multiq <: Question + choices + answer + values + labels + label + hint + inline +end + +""" + multiq(choices, answers; label="", hint="", keep_order=false) + +Multiple choice question (one *or more* of several) + +Arguments: + +* `choices`: indexable collection of choices. As seen in the example, choices can be formatted with markdown. + +* `answers::Vector{Int}`: index of correct choice(s) + +* `keep_order::Boolean`: if `true` keeps display order of choices, otherwise they are shuffled. + +* `inline::Bool`: hint to render inline (or not) if supported + +* `label`: optional label for the form element + +* `hint`: optional plain-text hint that can be seen on hover + +Example: + +``` +choices = ["pear", "tomato", "banana"] +answers = [1,3] +multiplecq(choices, answers; hint="not the red one!") +``` + +""" +function multiq(choices, answers; + label="", hint="", inline::Bool=(hint!=""), + keep_order::Bool=false) + inds = collect(1:length(choices)) + values = copy(inds) + labels = choices + !keep_order && shuffle!(inds) + + Multiq(choices[inds], findall(in(answers), inds), + values, labels[inds], label, hint, inline) +end + + """ booleanq(ans; [label, hint]) True of false questions: diff --git a/src/show_methods.jl b/src/show_methods.jl index 830fbc1..a687048 100644 --- a/src/show_methods.jl +++ b/src/show_methods.jl @@ -108,3 +108,33 @@ function Base.show(io::IO, m::MIME"text/html", x::Radioq) ) end + +function Base.show(io::IO, m::MIME"text/html", x::Multiq) + + ID = randstring() + + choices = string.(x.choices) + items = [_make_item(i, choice) for (i,choice) ∈ enumerate(choices)] + + GRADING_SCRIPT = Mustache.render(html_templates["multi_grading_script"]; + ID = ID, + CORRECT_ANSWER = x.answer, + INCORRECT = "Not yet", + CORRECT = "Correct" + ) + FORM = Mustache.render(html_templates["Multiq"]; + ID = ID, + ITEMS = items, + INLINE = x.inline ? " inline" : "" + ) + + 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