# -*- Tcl -*-
########################################################################
# In-class-quiz workflow, designed similar to online-exam
# ========================================================
#
# This teacher paced inclass quiz workflow lets a teacher choose from
# a predefined set of quiz questions. The teacher selects one or
# several quiz question via drag and drop.
#
# When the quiz is published, the students can be offered a display of
# the question with a QR code. Teacher can see the incoming answers in
# the report (without manual refresh when "live_updates" are
# activated). After every question the teacher can toggle to a
# "results" display showing the actual answer with statistics as
# provided by the participants.
#
# An admin might with to add the following entries to the folder to
# ease creation of exercises and exams
#
# {clear_menu -menu New}
#
# {entry -name New.Item.TextInteraction -form en:edit-interaction.wf -query p.item_type=Text}
# {entry -name New.Item.ShortTextInteraction -form en:edit-interaction.wf -query p.item_type=ShortText}
# {entry -name New.Item.SCInteraction -form en:edit-interaction.wf -query p.item_type=SC}
# {entry -name New.Item.MCInteraction -form en:edit-interaction.wf -query p.item_type=MC}
# {entry -name New.Item.ReorderInteraction -form en:edit-interaction.wf -query p.item_type=Reorder}
# {entry -name New.Item.UploadInteraction -form en:edit-interaction.wf -query p.item_type=Upload}
#
# {entry -name New.App.Quiz -label "Inclass Quiz" -form en:inclass-quiz.wf}
#
# The policy has to allow the following methods on FormPages:
#
# - "answer" (for students),
# - "edit" (for students),
# - "poll" (for students),
# - "delete" (for teachers),
# - "print-answers" (for teachers),
# - "qrcode" (for teachers)
#
# For fully functioning, one has to install qrencode
# $ apt install qrencode
#
#
# TODO:
# - full result exports
# * result export
# * and/or "print-answers" in inclass-quiz
# - word statistics for multiline text?
# - multiline in text_fields (short answers)
# - test cases
# - check mobile usability
#
# Gustaf Neumann, Nov 2019
########################################################################
set :autoname 1 ;# to avoid editable name field
set :policy ::xowf::test_item::test-item-policy-publish
set :debug 0
set :live_updates 1
Property position -default 0 -allow_query_parameter true
Action select -next_state created -label #xowf.inclass-quiz-create#
Action publish -next_state published -label #xowf.inclass-quiz-publish#
Action unpublish -next_state done -label #xowf.inclass-quiz-unpublish#
Action republish -next_state published -label #xowf.inclass-quiz-republish#
Action restart -next_state initial -label #xowf.restart#
Action show_results -next_state results -label #xowf.show_results#
State parameter {
{extra_css {/resources/xowf/test-item.css}}
}
State initial -actions {select} -form en:quiz-select_question.form -view_method edit
State created -actions {publish restart} -form_loader load_form -view_method edit \
-form "#xowf.inclass-quiz-draft#"
State published -actions {show_results unpublish} -form_loader load_form -view_method edit \
-form "#xowf.inclass-quiz-open#"
State results -actions {publish unpublish} -form_loader load_form -view_method edit \
-form "#xowf.inclass-quiz-open#"
State done -actions {republish restart} -form_loader load_form -view_method edit \
-form "#xowf.inclass-quiz-closed#"
########################################################################
# Activate action select: After the teacher has selected the
# exercises, the answer workflow is created.
#
select proc activate {obj} {
xowf::test_item::answer_manager create_workflow \
-answer_workflow /packages/xowf/lib/inclass-quiz-answer.wf \
$obj
}
########################################################################
# Activate action publish: delete all responses for the workflow.
#
publish proc activate {obj} {
#xowf::test_item::answer_manager delete_all_answer_data $obj
#:publish_link $obj
}
########################################################################
# Activate action republish: publish user participation link.
#
republish proc activate {obj} {
:publish_link $obj
}
########################################################################
# When the user un-publishes an exam, just the user participation
# link should be removed for the users
#
unpublish proc activate {obj} {
:unpublish_link $obj
}
########################################################################
# publish_link: make the user participation link available for the
# target group
#
Action instproc publish_link {obj} {
set aLink [$obj pretty_link -query m=answer]
util_user_message -html \
-message "[$obj name] is available as [ns_quotehtml $aLink]"
# TODO: make it happen
}
########################################################################
# unpublish_link: remove the user participation link for the target
# group.
#
Action instproc unpublish_link {obj} {
util_user_message -html -message "[$obj name] is closed"
# TODO: make it happen
}
########################################################################
# form loader: create dynamically a form containing the disabled
# questions as a preview and the survey results (the results can be
# refreshed). This is a simplified version of get_question_form_object
# of online-exam-answer.wf.
#
:proc load_form {ctx title} {
set obj [$ctx object]
set state [$obj property _state]
set wf [xowf::test_item::answer_manager get_answer_wf $obj]
switch $state {
"created" {
set combined_form_info [::xowf::test_item::question_manager combined_question_form \
-with_numbers $obj]
}
default {
set title [xowf::test_item::question_manager current_question_title -with_numbers $obj]
switch $state {
"published" {set title "#xowf.please_answer#: $title"}
"results" {set title "#xowf.results_of#: $title"}
}
set current_question [xowf::test_item::question_manager current_question_obj $obj]
set combined_form_info [::xowf::test_item::question_manager current_question_form $obj]
}
}
set fullQuestionForm [dict get $combined_form_info form]
set full_fc [dict get $combined_form_info disabled_form_constraints]
set qrCode ""
set answerStatus ""
switch $state {
"created" {
template::add_body_script -script urn:ad:js:bootstrap3
set fullQuestionForm [subst {
"
if {$wf eq ""} {
:msg "cannot get current workflow for [$obj name]"
set tLink "."
set aLink "."
set menu ""
} else {
set wf_pretty_link [$wf pretty_link]
set tLink "$wf_pretty_link?m=create-new&p.return_url=[::xo::cc url]&p.try_out_mode=1"
set lLink "$wf_pretty_link?m=list"
set aLink [$obj pretty_link -query m=answer]
set pLink [$obj pretty_link -query m=print-answers]
set pLink . ;# deactivated for the time being
#util_user_message -html -message "$survey is available as $pLink"
set menu [subst {\[#xowf.refresh#,
#xowf.inclass-quiz-quiz_instances#,
#xowf.print#\]}]
}
set extraAction ""
switch [$obj property _state] {
"created" {
#
# Deactivate try-out mode, since in the inclass quiz is designed
# in a way that teacher controls the pace of exercises. When the
# inclass-quiz is just published via QR-code, there is actually
# very little need to define a try-out mode....
#
#append extraAction " " \
# "#xowf.online-exam-try_out# " \
# "#xowf.testrun#"
}
"published" {
append extraAction " " \
"#xowf.online-exam-can_answer# " \
"$aLink"
}
}
# Remove wrapping forms
regsub -all {?form[^>]*>} $fullQuestionForm {} fullQuestionForm
append text [subst {
$answerStatus
$fullQuestionForm
$qrCode
}]
set wfName [$obj property wfName]
set footer " $menu $extraAction "
set f [::xowiki::Form new \
-destroy_on_cleanup \
-name en:question \
-form [subst { text/html}] \
-text {} \
-anon_instances t \
-form_constraints $full_fc \
]
}
########################################################################
#
# Object specific operations
#
########################################################################
:object-specific {
set ctx [:wf_context]
set container [$ctx wf_container]
if {$ctx ne $container} {
$ctx forward load_form $container %proc $ctx
}
${container}::Property return_url -default "" -allow_query_parameter true
::xo::cc unset_query_parameter return_url
########################################################################
#
# Setup actions
#
########################################################################
foreach action [${container}::Action info instances] {
if {[string is integer [namespace tail $action]]} {
$action destroy
}
}
if {${:state} in {published results done} } {
set questions [:property question]
set position [:property position]
set count 0
set actions {}
foreach question $questions {
incr count
${container}::Action create ${container}::$count \
-label "$count" \
-extra_css_class [expr {$position == $count - 1 ? "current" : ""}] \
-proc activate {obj} \
[list ::xowf::test_item::question_manager goto_page [self] [expr {$count -1}]]
lappend actions $count
}
#:msg "state?${:state} adding actions"
switch ${:state} {
"published" {lappend actions show_results unpublish}
"results" {lappend actions publish unpublish}
"done" {lappend actions republish restart}
}
${container}::${:state} actions $actions
}
switch ${:state} {
"results" { ${container}::publish label #xowf.inclass-quiz-publish_question# }
default { ${container}::publish label #xowf.inclass-quiz-publish# }
}
########################################################################
# Extern callable methods
########################################################################
########################################################################
# web-callable method "delete"
#
# Delete the workflow instance and all its associated data.
#
:proc www-delete {} {
xowf::test_item::answer_manager delete_all_answer_data [self]
next
}
########################################################################
# web-callable method "print-answers"
#
# Print the answers in a somewhat printer friendly way.
#
:proc www-print-answers {} {
set HTML ""
set wf [xowf::test_item::answer_manager get_answer_wf [self]]
if {$wf ne ""} {
set items [xowf::test_item::answer_manager get_wf_instances $wf]
set examTitle ${:title}
foreach i [$items children] {
set uid [$i property _creation_user]
set userName [acs_user::get_element -user_id $uid -element username]
set time [::xo::db::tcl_date [$i property _last_modified] tz_var]
set pretty_date [clock format [clock scan $time] -format "%Y-%m-%d %T"]
#
# The call to "render_content" calls actually the
# "summary_form" of the *-answer.wf when the submit
# instance is in state "done". We set the __feedback_mode to
# get the auto-correction included.
#
$i set __feedback_mode 2
set question_form [$i render_content]
append HTML "\n