topic-assignment.wf
Delivered as */*
[ hide source ] | [ make this the default ]
File Contents
# -*- Tcl -*- ######################################################################## # Topic-assignment workflow, designed similar to online-exam # ========================================================== # # Defining topic-selection-survey: This workflow lets a lecturer # choose from a some ordering exercises used to collect preferences of # participants. The lecturer selects lists with topics via drag and # drop. The lecturer can perform a test run of the created survey, and # can get the results via a result table. # # Publishing and closing survey: When a lecturer is satisfied with the # survey, the survey can be published. In this step, all answers of # the testing phase are deleted. In the process of publishing, the # link to start the topic selection survey is offered to the user. # When the survey is published, the lecturer can see the incoming # answers in the report by refreshing the page. When the survey is done, # it is unpublished. The workflow offers the lecturer to see a summary # of the results in form of a table (an to download the results via # csv), or the lecturer can produce a printer friendly version of the # answers. # # An admin might wish to add the following entries to the folder to # ease creation of questions and workflows # # {config -use test-items} # # The policy has to allow the following methods on FormPages: # # - "answer" (for students), # - "proctor" (for students), # - "view-my-exam" (for students), # - "edit" (for students), # - "poll" (for lecturers), # - "print-answers" (for lecturers), # - "print-answer-table" (for lecturers), # - "print-participants" (for lecturers), # - "delete" (for lecturers), # - "qrcode" (for lecturers) # # Gustaf Neumann, 2021 ######################################################################## set :autoname 1 ;# to avoid editable name field set :policy ::xowf::test_item::test-item-policy-publish set :debug 0 set :live_updates 1 Action select -next_state created -label "Create Topic Assignment Survey" Action publish -state_safe true -next_state published -label "Publish Survey" Action unpublish -state_safe true -next_state done -label "Close Survey" Action republish -next_state published -label "Reopen Topic Assignment Survey" \ -title #xowf.online-exam-title-republish# Action groupformation -next_state buildgroups -label "Group Formation" Action todone -next_state done -label "Survey Overview" Action restart -next_state initial -label "Restart" Action assign -next_state done -label "Assign Students to Groups" State parameter { {extra_css {/resources/xowf/test-item.css}} {view_method edit} } State initial \ -actions {select} \ -form en:select-topics.form State created \ -actions {publish restart} \ -form_loader load_form \ -form "Survey not published" State published \ -actions {unpublish} \ -form_loader load_form \ -form "Survey is published" State done \ -actions {republish groupformation} \ -in_role swa { -actions {republish groupformation restart} } \ -form_loader load_form \ -form "Topic Assignment Survey is closed" State buildgroups \ -actions {assign todone} \ -form_loader group_parameter_form \ -form "Group assignment parameters" ######################################################################## # Activate action select: After the lecturer has selected the # exercises, the answer workflow is created. # select proc activate {obj} { set question [$obj property question] if {[$obj property with_team_preferences 0]} { set extra_form en:select-group-members.form if {$extra_form ni $question} { $obj set_property question [lappend question $extra_form] } } elseif {[llength $question] > 1} { # # There was a change in the configuration, with_team_preferences # was probably deactivated. # $obj set_property question [lindex $question 0] } xowf::test_item::answer_manager create_workflow \ -answer_workflow /packages/xowf/lib/topic-assignment-answer.wf \ $obj } ######################################################################## # Activate action publish: delete all responses for the workflow and # publish user participation link. # 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 } ######################################################################## # When the user restarts an exam, make sure that already scheduled # atjobs are removed. # restart proc activate {obj} { xowf::test_item::answer_manager delete_scheduled_atjobs $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] $obj util_user_message -html \ -message "[$obj name] is available as <a target='_blank' href='[ns_quotehtml $aLink]'>[ns_quotehtml $aLink]</a>" # TODO: make it happen in the LMS } ######################################################################## # unpublish_link: remove the user participation link for the target # group # Action instproc unpublish_link {obj} { $obj util_user_message -html -message "[$obj name] is closed</a>" # TODO: make it happen in the LMS } ######################################################################## # form loader: create dynamically a form containing the disabled # questions as a preview and the survey results (the results can be # refreshed). # :proc load_form {ctx title} { set obj [$ctx object] set state [$obj property _state] set combined_form_info [::xowf::test_item::question_manager combined_question_form -with_numbers $obj] set fullQuestionForm [dict get $combined_form_info form] set full_fc [dict get $combined_form_info disabled_form_constraints] #:log fullQuestionForm=$fullQuestionForm set text "<h3>$title</h3>" set menu "" set synchronized [$obj property synchronized 0] set with_team_preferences [$obj property with_team_preferences 0] set question_objs [dict get $combined_form_info question_objs] set nrQuestions [llength $question_objs] append text [subst {<p> [expr {$synchronized ? "" : "Non-"}]Synchronized Survey<br> Team preferences [expr {$with_team_preferences ? "actviated" : "not activated"}] </p>}] set revision_sets [$obj get_revision_sets] set published_periods [xowf::test_item::answer_manager state_periods $revision_sets -state published] set max_items_msg "" append text [subst { <p> [expr {[llength $published_periods] > 0 ? "<br>Survey published: [join $published_periods {, }]<br>" : ""}] </p> }] set wf [xowf::test_item::answer_manager get_answer_wf $obj] if {$wf eq ""} { :msg "cannot get current workflow for [$obj name]" set lLink "." set tLink "." set aLink "." set pLink "." } else { # # Always compute the test-run and answer link. # set wf_pretty_link [$wf pretty_link] set tLink [export_vars -base $wf_pretty_link { {m create-new} {p.return_url "[::xo::cc url]"} {p.try_out_mode 1} {title "[$obj title]"} }] set aLink [$obj pretty_link -query m=answer] # # If there are answers, include the full menu. # set answers [xowf::test_item::answer_manager get_answers $wf] if {[llength $answers] > 0} { set lLink "$wf_pretty_link?m=list" set pLink1 [$obj pretty_link -query m=print-answers] set pLink2 [$obj pretty_link -query m=print-answer-table] set pLink3 [$obj pretty_link -query m=print-participants] set menu "\[" if {[acs_user::site_wide_admin_p -user_id [::xo::cc user_id]]} { append menu "<a href='[ns_quotehtml $lLink]'>#xowf.online-exam-exam_instances#</a>, " } append menu \ "<a href='[ns_quotehtml $pLink3]'>#xowf.Participants#</a>, " \ "<a href='[ns_quotehtml $pLink1]'>#xowf.online-exam-protocol#</a>, " \ "<a href='[ns_quotehtml $pLink2]'>#xowf.online-exam-results-table#</a>\]" } } switch $state { "created" - "done" - "published" { # # In inclass cases, never show all questions on screen, since # the lecturer might have the screen on the projector. # template::add_script -src urn:ad:js:bootstrap3 set fullQuestionForm [subst { <button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="collapse" data-target="#questions">Topics <span class="caret"></span></button> <div id="questions" class="collapse"> $fullQuestionForm </div> }] } } set extraAction "" switch $state { "created" { append extraAction "<br>" \ "#xowf.online-exam-try_out# " \ "<a class='btn btn-default' href='[ns_quotehtml $tLink]' target=”_blank”>#xowf.testrun#</a>" } "published" { append extraAction "<br>" \ "#xowf.online-exam-can_answer# " \ "<a href='$aLink'>$aLink</a>" } } set www_method [xo::cc query_parameter m:token] if {$www_method ni {edit view}} { set marked "" } else { set answerStatus "" set marked "" if {$state in {published done} } { if {$state eq "done"} { [$ctx object] setCSSDefaults set marked [xowf::test_item::answer_manager marked_results -obj $obj -wf $wf $combined_form_info] set marked "" ;# not needed right now } set answerStatus [xowf::test_item::answer_manager answers_panel \ -heading "Submitted Preferences" \ -submission_msg "Preferences provided by students" \ -polling=[expr {${:live_updates} && $state ni {initial created done}}] \ -manager_obj $obj \ -target_state done \ -wf $wf] } set qrCode "" set countdownHTML "" if {$state eq "published"} { set src [$obj pretty_link -query m=qrcode] set qrCode [subst {<div><img class="img-thumbnail qrcode" src="[ns_quotehtml $src]" ></div>}] set target_time [xowf::test_item::question_manager exam_target_time \ -manager $obj -base_time [$obj last_modified]] set countdownHTML [xowf::test_item::answer_manager countdown_timer \ -target_time $target_time -id "countdown"] } # Remove wrapping forms regsub -all {</?form[^>]*>} $fullQuestionForm {} fullQuestionForm append text [subst { <div class='container-fluid'><div class='row'> <div class="col-sm-12">$answerStatus</div> <div class="col-sm-9 quiz-preview">$fullQuestionForm</div> <div class="col-sm-3">$qrCode</div> <div class="col-sm-6">$countdownHTML</div> </div></div> }] } set footer "$menu $extraAction" if {$state eq "done"} { lappend full_fc \ {maxgroupsize:number,form_item_wrapper_CSSclass=form-inline,min=0,default=3,label=Max Students per group} \ {mingroupsize:number,form_item_wrapper_CSSclass=form-inline,min=0,default=,label=Min Students per group} \ {allowemptygroups:boolean,horizontal=true,default=t,label=Allow empty groups} set groupformation "<p><hr><h4>Group formation parameters</h4> \ @maxgroupsize@ @mingroupsize@ @allowemptygroups@" } else { set groupformation "" } #-form [subst {<form>$text<div class='exam-preview'>$fullQuestionForm</div>$report</form> text/html}] set f [::xowiki::Form new \ -destroy_on_cleanup \ -set name en:question \ -form [subst {<form>$text$marked$groupformation$footer</form> text/html}] \ -text {} \ -anon_instances t \ -form_constraints $full_fc \ ] } :proc group_parameter_form {ctx title} { set obj [$ctx object] set state [$obj property _state] set wf [xowf::test_item::answer_manager get_answer_wf $obj] set answers [xowf::test_item::answer_manager get_answers -extra_attributes {_creation_user members} $wf] set nrgroups [llength [lindex $answers 0 3 1]] set nrParticipants [llength $answers] set report "nrParticipants=$nrParticipants\nnrGroups=$nrgroups\n" foreach p {maxgroupsize mingroupsize allowemptygroups} { append report "$p: [$obj property $p]\n" } append report \n set combined_form_info [::xowf::test_item::question_manager combined_question_form -with_numbers $obj] set question_objs [dict get $combined_form_info question_objs] set labels {} # # The first question_obj has to be to topics # set topics_form [lindex $question_objs 0] if {$topics_form ne ""} { set q [dict get [$topics_form instance_attributes] question] foreach {key text} [dict get $q question.interaction question.interaction.answer] { if {[regexp {[.]([^.]+)$} $key _ nr]} { #append report "$nr: $text\n" dict set labels g$nr $text } } } set select_group_members_form [lindex $question_objs 1] if {$select_group_members_form ne ""} { #ns_log notice "[$select_group_members_form serialize]" ns_log notice "ANSWERS $answers" } set team_preferences "" foreach d $answers { set atts [dict get $d answerAttributes] set i [dict get $d item] dict set team_preferences [$i creation_user] [regsub -all \n [dict get $atts members] " "] } foreach s [dict keys $team_preferences] { set filtered_prefs {} foreach pref [dict get $team_preferences $s] { if {$pref == $s} { continue } if {[dict exists $team_preferences $pref]} { set others_prefs [dict get $team_preferences $pref] if {$s in $others_prefs} { ns_log notice "reciprocal pref $s -> $pref and $pref -> $s" lappend filtered_prefs $pref } else { ns_log notice "preference is not reciprocal" } } else { ns_log notice "no preference for $pref recorded" } } ns_log notice "set team preferences for $s to $filtered_prefs" dict set team_preferences $s $filtered_prefs } set students {} foreach d $answers { set atts [dict get $d answerAttributes] set i [dict get $d item] set user_id [$i creation_user] set user [acs_user::get_element -user_id $user_id -element username] set name [::xo::get_user_name $user_id] set preferences [lmap x [lindex $atts 1] {incr x}] set team [dict get $team_preferences $user_id] lappend students [list -user "$user $name" -preferences $preferences -user_id $user_id -team $team] } append report "-students \{\n " [join $students "\n "] "\n\}\n" set cmd [list ::xowf::StudentAssignment new -nrgroups $nrgroups] foreach p {maxgroupsize mingroupsize} { set value [$obj property $p] if {$value ne ""} { lappend cmd -$p $value } } if {[$obj property allowemptygroups 0]} { lappend cmd -allowemptygroups } lappend cmd -students $students #append report "\n$cmd\n" if {0} { set model [ {*}$cmd ] set r [$model run] #ns_log notice "================= label <$labels>" append report [$model report -grouplabels $labels $r] $model destroy } #append report [dict get $r annotated] \n #append report [dict get $r best] \n # # Avoid interpretation as includelet, when the report contains # double curley braces. # regsub -all "(\{\{)" $report {\\\1} report if {0} { StudentAssignment create a000 \ -maxgroupsize 3 \ -mingroupsize 2 \ -nrgroups 5 \ -allowemptygroups \ -students { {-user {neumann@wu-wien.ac.at} -preferences {4 3 2 5 1}} {-user {gustaf.neumann@wu.ac.at} -preferences {5 1 2 3 4}} {-user {u1} -preferences {4 3 2 5 1}} {-user {u2} -preferences {5 1 2 3 4}} } } set f [::xowiki::Form new \ -destroy_on_cleanup \ -set name en:question \ -form [subst {<form><h4>$title</h4><pre>$report</pre></form> text/html}] \ -text {} \ -anon_instances t \ -form_constraints { @cr_fields:hidden _description:omit _page_order:omit } \ ] } ######################################################################## # # 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 $ctx forward group_parameter_form $container %proc $ctx } ${container}::Property return_url -default "" -allow_query_parameter true if {${:state} eq "done"} { set done_actions {republish groupformation} set combined_form_info [::xowf::test_item::question_manager combined_question_form [self]] set swa_done_actions [concat $done_actions restart] ${container}::done actions $done_actions ${container}::done in_role swa [subst { -actions {$swa_done_actions} }] } # # Unset the actual query return_url, since we want to use it via # property. In some cases, we have to set it explicitly from the # property, e.g. in www-delete. # ::xo::cc unset_query_parameter return_url ######################################################################## # web-callable method "delete" # # Delete the workflow instance and all its associated data. # :proc www-delete {} { ::xo::cc set_query_parameter return_url [:property return_url] xowf::test_item::answer_manager delete_all_answer_data [self] next } ######################################################################## # web-callable method "print-answer-table" # # Print the answers in a tabular form. # :proc www-print-answer-table {} { set HTML "" set withAnswerColumns [${:package_id} query_parameter with_answers:boolean 0] set wf [xowf::test_item::answer_manager get_answer_wf [self]] if {$wf ne ""} { #set form_info [::xowf::test_item::question_manager combined_question_form -with_numbers [self]] set items [xowf::test_item::answer_manager get_wf_instances $wf] set items2 [$items deep_copy] foreach i [$items2 children] { $i set online-exam-userName [acs_user::get_element -user_id [$i creation_user] -element username] $i set online-exam-fullName [::xo::get_user_name [$i creation_user]] } set HTML [::xowf::test_item::answer_manager results_table \ -package_id ${:package_id} \ -items $items2 \ -state * \ -with_answers $withAnswerColumns \ [self]] $items2 destroy } if {$HTML eq ""} { set HTML "#xowiki.no_data#" } else { set HTML "<h1>#xowf.online-exam-results-table#</h1>$HTML" } set return_url [[$wf package_id] query_parameter local_return_url:localurl [:pretty_link]] append HTML "<hr><p><a class='btn btn-default' href='$return_url'>#xowiki.back#</a></p>\n" xo::Page requireCSS /resources/xowf/test-item.css :www-view $HTML } ######################################################################## # web-callable method "print-participants" # # Print participants in a tabular form. # :proc www-print-participants {} { 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 items2 [$items deep_copy] foreach i [$items2 children] { $i set online-exam-userName [acs_user::get_element -user_id [$i creation_user] -element username] $i set online-exam-fullName [::xo::get_user_name [$i creation_user]] } set HTML [::xowf::test_item::answer_manager participants_table \ -package_id ${:package_id} \ -items $items2 \ -state * \ [self]] $items2 destroy } if {$HTML eq ""} { set HTML "#xowiki.no_data#" } else { set HTML "<h3>#xowf.Participants#</h3>$HTML" } set return_url [[$wf package_id] query_parameter local_return_url:localurl [:pretty_link]] append HTML "<hr><p><a class='btn btn-default' href='$return_url'>#xowiki.back#</a></p>\n" :www-view $HTML } ######################################################################## # web-callable method "print-answers" # # Print the answers in a somewhat printer friendly way. # :proc www-print-answers {} { set as_student [:query_parameter as_student:boolean 0] set filter_id [:query_parameter id:int32 ""] set creation_user [:query_parameter creation_user:int32 ""] set revision_id [:query_parameter rid:int32 ""] set export [:query_parameter export:boolean 0] set combined_form_info [::xowf::test_item::question_manager combined_question_form [self]] set totalPoints [::xowf::test_item::question_manager total_points \ -max_items [:property max_items ""] \ $combined_form_info] # # The management of the grading scheme has to be extended. For the # time being, we have a single grading scheme with the option to # round to full points or not. When an exam has less than 40 # points, we do not round, since this rounding could provide more # than 1 percent of the result. This should be made configurable # (also in www-print-answer-table, which is not used right now). # set grading_scheme ::xowf::test_item::grading::wi1 if {$totalPoints < 40} { append grading_scheme _noround } set grade_dict {} set grade_csv "" # # Provide quick mapping from the mangled attribute name to the question obj. # set nameToQuestionObj [xowf::test_item::renaming_form_loader \ name_to_question_obj_dict \ [dict get $combined_form_info question_objs]] set ctx [::xowf::Context require [self]] set wf [xowf::test_item::answer_manager get_answer_wf [self]] if {$wf ne ""} { set items [xowf::test_item::answer_manager get_wf_instances \ {*}[expr {$creation_user ne "" ? "-creation_user $creation_user" : ""}] \ {*}[expr {$filter_id ne "" ? "-item_id $filter_id" : ""}] \ $wf] set withSignature [expr {[dict exists ${:instance_attributes} signature] ? [dict get ${:instance_attributes} signature] : 0 }] set examTitle ${:title} set do_stream [expr {[llength [$items children]] > 100}] # # Add a simple print button for the unaware that makes it easy # to print the exam protocol to PDF and use e.g. a pdf-tool to # annotate free text answers. # set HTML { <button id="print-button"> <span class='glyphicon glyphicon-print' aria-hidden='true'></span> print </button> } template::add_event_listener \ -id print-button \ -event click \ -preventdefault=false \ -script "window.print();" append HTML [template::collect_body_scripts] ::xo::cc set_parameter template_file view-plain-master ::xo::cc set_parameter MenuBar 0 template::head::add_link -rel stylesheet -href /resources/xowf/test-item.css if {$as_student} { set userName [acs_user::get_element -user_id [ad_conn user_id] -element username] set fullName [::xo::get_user_name [ad_conn user_id]] set heading "$userName - $fullName" append HTML "<h2>#xowf.online-exam-review-protocol# - $heading</h2>\n" } else { append HTML "<h2>#xowf.online-exam-protocol#</h2>\n" } if {$do_stream} { # ns_log notice STREAM-[info level]-$::template::parse_level uplevel #$::template::parse_level [subst {set title "${:title}"; set context .}] ad_return_top_of_page [ad_parse_template \ -params [list context title] \ [template::streaming_template]] ns_write [subst { <div class=''main-content> <div class='xowiki-content' style='padding-left:15px;'> <h1>[ns_quotehtml ${:title}]</h1> [lang::util::localize $HTML] }] set HTML "" } if {$revision_id ne ""} { set r [::xowiki::FormPage get_instance_from_db -revision_id $revision_id] if {[$r item_id] ni [lmap i [$items children] {$i item_id}]} { error "invalid revision id '$revision_id' provided" } $items destroy set items [::xo::OrderedComposite new -destroy_on_cleanup] $items add $r } if {$export} { set recutil [xowf::test_item::answer_manager recutil_create \ -clear \ -exam_id [$wf parent_id] \ -fn [expr {$filter_id eq "" ? "all.rec" : "$filter_id.rec"}] ] } foreach i [$items children] { $i set online-exam-userName [acs_user::get_element -user_id [$i creation_user] -element username] $i set online-exam-fullName [::xo::get_user_name [$i creation_user]] } $items orderby online-exam-userName foreach i [$items children] { set userName [$i set online-exam-userName] set fullName [$i set online-exam-fullName] set state [$i state] #if {$state ne "done"} { # ns_log notice "online-exam: submission of $userName is not finished (state $state)" # #continue #} set revisions [$i get_revision_sets] if {[llength $revisions] <=1 } { # just an initial revision ns_log notice "online-exam: submission of $userName is empty. Ignoring." continue } # # The call to "render_content" calls actually the # "summary_form" of online/inclass-exam-answer.wf when the submit # instance is in state "done". We set the __feedback_mode to # get the auto-correction included. # foreach f [::xowiki::formfield::FormField info instances -closure] { #ns_log notice "FF could DESTROY $f [$f name]" if {[string match *_ [$f name]]} { #ns_log notice "FF DESTROY $f [$f name]" $f destroy } } $wf form_field_flush_cache set achieved_points {} xo::cc eval_as_user -user_id [$i creation_user] { $i set __feedback_mode 2 set question_form [$i render_content] if {$export} { xowf::test_item::answer_manager export_answer \ -user_answers $i \ -html $question_form \ -combined_form_info $combined_form_info \ -recutil $recutil } if {$withSignature} { set answerAttributes [xowf::test_item::renaming_form_loader \ answer_attributes [$i instance_attributes]] } } if {$withSignature} { set sha256 [ns_md string -digest sha256 $answerAttributes] set signatureString "<div class='signature'>online-exam-actual_signature: $sha256</div>\n" set submissionSignature [$i property signature ""] if {$submissionSignature ne ""} { append signatureString "<div>#xowf.online-exam-submission_signature#: $submissionSignature</div>\n" } } else { set signatureString "" } set time [::xo::db::tcl_date [$i property _last_modified] tz_var] set pretty_date [clock format [clock scan $time] -format "%Y-%m-%d"] set view [expr {$as_student ? "student" : $filter_id ne "" ? "revision_overview" : "default"}] set gradingInfo [$grading_scheme print -achieved_points $achieved_points] set grandingPanel [expr {[dict exists $gradingInfo panel] ? [dict get $gradingInfo panel] : ""}] set runtime_panel [xowf::test_item::answer_manager runtime_panel \ -revision_id $revision_id \ -view $view \ -grading_info $grandingPanel \ $i] set heading "$userName · $fullName · $pretty_date" append HTML [subst { <div class='single_exam'> <div class='runtime-data'> [expr {$as_student ? "" : "<h2>$heading</h2>"}] $runtime_panel </div> $signatureString $question_form </div> }] if {$do_stream} { ns_write [lang::util::localize $HTML] set HTML " " } } if {$export} { $recutil destroy } } if {$HTML eq ""} { append HTML "#xowiki.no_data#" } if {!$as_student} { set return_url [:query_parameter local_return_url:localurl [:pretty_link]] append HTML "<hr><p><a class='btn btn-default' href='$return_url'>#xowiki.back#</a></p>\n" } #::xo::cc set_parameter template_file view-plain-master #::xo::cc set_parameter MenuBar 0 #xo::Page requireCSS /resources/xowf/test-item.css if {$do_stream} { ns_write [lang::util::localize $HTML] set HTML "" [$wf package_id] set __continuation ad_progress_bar_end return "" } else { :www-view $HTML } } ######################################################################## # web-callable method "answer" # :proc www-answer {} { # # Create or use an answering workflow for the current exam. This # is a convenience routine to shorten the published URL. # # Make sure that no-one tries to start the answer workflow in a # state different to "published". # if {[:property _state] ne "published"} { :util_user_message -html -message "Cannot start answer workflow in this state" } else { set wf [xowf::test_item::answer_manager get_answer_wf [self]] set proctoring [:property proctoring] if {$proctoring ne "" && $proctoring} { set po [:property proctoring_options] set cLink [export_vars -base [:pretty_link] { {m proctor} {link "[:pretty_link -query m=proctor-answer&proctoring_options=$po]"} }] ::${:package_id} returnredirect $cLink } else { $wf www-create-or-use -parent_id [:item_id] } } } :proc www-proctor-answer {} { # # Start answering an exam in proctored mode # if {[:property _state] ne "published"} { :util_user_message -html -message "Cannot start answer workflow in this state" } else { set wf [xowf::test_item::answer_manager get_answer_wf [self]] $wf www-create-or-use -parent_id [:item_id] } } :proc www-qrcode {} { # # Produce a QR code with an answer link # set aLink [:pretty_link -absolute true -query m=answer] set fn /tmp/qr-${:item_id}.png exec qrencode -o $fn -l h $aLink ns_returnfile 200 image/png $fn ad_script_abort } ######################################################################## # AJAX call "poll" # :proc www-poll {} { # # Return statistics about working and finished exams. # set wf [xowf::test_item::answer_manager get_answer_wf [self]] set answers [xowf::test_item::answer_manager get_answers $wf] set answered [xowf::test_item::answer_manager get_answers -state done $wf] ns_return 200 text/plain [llength $answered]/[llength $answers] #ns_log notice "MASTER POLL [self] ${:name}, returned [llength $answered]/[llength $answers]" ad_script_abort } ######################################################################## # AJAX call "send-participant-message" # :proc www-send-participant-message {} { # # Send a message to a participant # ::xowiki::includelet::personal-notification-messages message_add \ -notification_id ${:item_id} \ -to_user_id [${:package_id} query_parameter user_id:int32 ""] \ -payload [list msg [ns_queryget msg] from [xo::cc user_id] urgency [ns_queryget urgency]] ns_return 200 text/plain ok ad_script_abort } } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: