Index: openacs-4/packages/xowf/xowf.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/xowf.info,v diff -u -N -r1.12.2.9 -r1.12.2.10 --- openacs-4/packages/xowf/xowf.info 14 Feb 2020 13:42:12 -0000 1.12.2.9 +++ openacs-4/packages/xowf/xowf.info 21 Feb 2020 13:45:15 -0000 1.12.2.10 @@ -10,15 +10,16 @@ t xowf - + Gustaf Neumann XoWiki Content Flow - an XoWiki based workflow system implementing state-based behavior of wiki pages and forms 2017-08-06 WU Vienna 2 - + + Index: openacs-4/packages/xowf/catalog/xowf.de_DE.ISO-8859-1.xml =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/catalog/xowf.de_DE.ISO-8859-1.xml,v diff -u -N -r1.2.2.11 -r1.2.2.12 --- openacs-4/packages/xowf/catalog/xowf.de_DE.ISO-8859-1.xml 15 Feb 2020 10:38:09 -0000 1.2.2.11 +++ openacs-4/packages/xowf/catalog/xowf.de_DE.ISO-8859-1.xml 21 Feb 2020 13:45:15 -0000 1.2.2.12 @@ -3,8 +3,17 @@ Beispiel Bewertungsschema - Bewertung + Bewertung + Zuf�llige Reihenfolge + Tage + Tag Minuten + Minute + Stunden + Stunde + Sekunden + Sekunde + verbleibend Alternative Richtig! Falsch. @@ -29,40 +38,46 @@ Pr�fung ist freigeschaltet Pr�fung ist geschlossen Frage - Fragen - Erste Frage + Fragen + Erste Frage N�chste Frage - Vorherige Frage + Vorherige Frage Zur�ck zum Start Finale Abgabe Antwort zwischenspeichern Pr�fung beginnen Zusammenfassung der Antworten - Pr�fung verlassen - Erstelle Pr�fung + Pr�fung verlassen + Erstelle Pr�fung Schalte Pr�fung frei Schlie�e Pr�fung Schalte Pr�fung nochmals frei Aktualisieren Drucken + Signatur + Erzeuge einen digitalen Fingerabdruck bei Pr�fungsabgabe als Beleg der unver�nderten Abgabe + Aktuelle Signatur - Abgegebene Signatur + Abgegebene Signatur Liste der Bearbeitungen Wollen Sie einen Probelauf durchf�hren? - Studierende k�nnen die Fragen mit folgendem Link beantworten + Studierende k�nnen die Fragen mit folgendem Link beantworten Probelauf Die Pr�fung ist von der Aufsicht nicht freigegeben! Die Pr�fungszeit ist beendet Pr�fungsprotokoll Ergebnisse Abgegebene Pr�fungen Studierende haben eine Pr�fung abgegeben - Teilnehmer + Bezeichung der Pr�fung + Pr�fung aus ... + + Teilnehmer Teilnehmer haben diese Frage beantwortet Bitte beantworten Sie Ergebnisse - Zeige Antwortstatisktik + Zeige Antwortstatisktik Ergebnisse von Quiz beenden Antwort speichern @@ -73,10 +88,21 @@ Warte auf n�chste Frage ... Der Quiz kann derzeit nicht beantwortet werden Sie haben Frage %number% beantwortet - Zufallsauswahl + Bezeichung des Quiz + Quiz ... + + Abgabe def Pr�fung + Pr�fung ist abgegeben + + W�hlen Sie Fragen mittels Drag&Drop aus + + Randomisierte Fragenfolge + Zeige die Fragen der Pr�fung f�r jeden Studierenden in einer anderen Reihenfolge + + Zufallsauswahl Nie Pro Benutzer - Immer + Immer Kurztextfrage Single-Choice-Frage @@ -89,7 +115,11 @@ Textfrage Kurztextfrage MC-Frage - SC-Frage - Dateiabgabefrage + SC-Frage + Dateiabgabefrage + + Entwurf des Inclass-Exams (nicht freigegeben) + Inclass-Exam ge�ffnet (Zur Bearbeitung freigegeben) + Inclass-Exam geschlossen Index: openacs-4/packages/xowf/catalog/xowf.en_US.ISO-8859-1.xml =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/catalog/xowf.en_US.ISO-8859-1.xml,v diff -u -N -r1.2.2.10 -r1.2.2.11 --- openacs-4/packages/xowf/catalog/xowf.en_US.ISO-8859-1.xml 15 Feb 2020 10:38:09 -0000 1.2.2.10 +++ openacs-4/packages/xowf/catalog/xowf.en_US.ISO-8859-1.xml 21 Feb 2020 13:45:15 -0000 1.2.2.11 @@ -3,9 +3,17 @@ Exercise Grading Scheme - Score - Shuffle + Score + Shuffle + Days + Day Minutes + Minute + Hours + Hour + Seconds + Second + remaining Alternative That is correct! Sorry, incorrect! @@ -31,49 +39,55 @@ Exam closed Answer Question - Questions - First Question + Questions + First Question Next Question - Previous Question + Previous Question Restart Refresh Print Testrun Please answer - Participant + Participant participants answered this question Results - Show Results + Show Results Results of Actual Signature - Submission Signature - + Submission Signature + + Signature + Provide a digital finger print of submitted answers as proof of submitted content + Create Exam Review Exam Final submission Save Answer Start Exam - Leave Exam + Leave Exam Publish Exam Close Exam - Publish Exam again - Publish exam again, existing answers are NOT deleted. - Publish exam (already existing answers will be deleted) - Go back to start where questions can be selected (existing anwers will be deleted). + Open Exam again + Publish exam again, existing answers are NOT deleted. + Publish exam (already existing answers will be deleted) + Go back to start where questions can be selected (existing anwers will be deleted). Create a fresh Exam (will delete all answers to this exam) Listing of Filled-out Exams Do you want to try out the exam? - Students can now answer via - + Students can now answer via + The Exam has not been published The Exam is finished Exam Protocol Results Submitted exams - Persons have submitted exam + Persons have submitted exam + Name of Exam + Exam ... + The quiz has not been published The quiz is open The quiz is closed @@ -89,13 +103,23 @@ The quiz is currently not available for answering You have already answered question %number% Listing of Submitted Quizzes + Name of Quiz + Quiz ... + Drag and Drop Questions ... + + Randomized Item Order + Present question of exam to every student in different order + + Submit and Finish Exam + You have finished this exam. + Short Text Question Single Choice Question Multiple Choice Question Text Question File Upload Question - Sub-Question + Sub-Question Show Max None @@ -106,10 +130,16 @@ Short Text Interaction MC Interaction SC Interaction - Upload Interaction + Upload Interaction Online Exam Inclass Quiz - Inclass Exam + Inclass Exam + Draft Inclass Exam (not published) + Inclass Exam open (published) + Inclass Exam closed + + Created Questions + Created Assessments Index: openacs-4/packages/xowf/lib/inclass-exam-answer.wf =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/lib/Attic/inclass-exam-answer.wf,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/lib/inclass-exam-answer.wf 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,391 @@ +# -*- Tcl -*- +# +# Workflow template for answering inclass exams. The workflow is +# typically controlled from a parent workflow that a teacher can use +# to create the exam, to try it out and to publish it +# (online-exam.wf). +# +# This workflow is based on the test-item infrastructure using +# the "renaming_form_loader" and "question_manager". +# + +set :autoname 1 ;# to avoid editable name field +set :policy ::xowf::test_item::test-item-policy-answer +set :debug 0 + +######################################################################## +# +# Properties +# +# position: the current page in the exam +# return_url: when the exam is finished, the user proceeds to this url +# try_out_mode: a teacher can try the exam in this mode +# ip: IP address of the user, kept in the instance attribute for auditing +# +######################################################################## + +Property position -default 0 +Property return_url -default "" -allow_query_parameter true +Property try_out_mode -default 0 -allow_query_parameter true + +######################################################################## +# +# Action definitions +# +######################################################################## + +Action allocate -proc activate {obj} { + # Called, when we try to create or use a workflow instance + # via a workflow definition ($obj is a workflow definition) + set parent_id [$obj parent_id] + set name [ns_md5 $parent_id-[::xo::cc set untrusted_user_id]] + set parent_obj [::xo::db::CrClass get_instance_from_db -item_id $parent_id] + :payload [list title [$parent_obj title] name $name] +} + +Action initialize -proc activate {obj} { + # called, after workflow instance was created + + # make sure to create the parent (the controlling workflow) + set parent_obj [::xo::db::CrClass get_instance_from_db -item_id [$obj parent_id]] + set parent_state [$parent_obj state] + + # + # Don't allow one to enter values when the state of the parent + # workflow is not published (the teacher has not published the exam, + # or closed it already). Only allow usage in the try-out-mode. + # + if {$parent_state ne "published" && [$obj property try_out_mode 0] == 0} { + set current_state [$obj property _state] + set locking_state [expr {$current_state eq "initial" ? "initial" : "done"}] + set locking_msg(initial) "#xowf.online-exam-not-published#" + set locking_msg(done) "#xowf.online-exam-finished#" + + util_user_message -message $locking_msg($locking_state) + # + # Force the user in the done state. Alternatively, we could + # handle this in the provide a different form or push the user to some other state. + # + [:wf_context] set_current_state $locking_state + } +} + +Action instproc goto_page {position} { + :set_property position $position +} +Action instproc set_page {obj increment} { + set parent_obj [::xo::db::CrClass get_instance_from_db -item_id [$obj parent_id]] + set pages [::xowf::test_item::question_manager question_names $parent_obj] + set position [:property position 0] + incr position $increment + if {$position < 0} { + set position 0 + } elseif {$position >= [llength $pages]} { + set position [expr {[llength $pages] - 1}] + } + :goto_page $position +} + +Action prevQuestion \ + -next_state working \ + -label #xowf.previous_question# \ + -proc activate {obj} {:set_page $obj -1} + +Action nextQuestion \ + -next_state working \ + -label #xowf.next_question# \ + -proc activate {obj} {:set_page $obj 1} + +Action review \ + -next_state done \ + -label #xowf.online-exam-review# \ + -proc activate {obj} { + [[$obj wf_context ] wf_container] addSignature $obj + } + +Action save \ + -label #xowf.online-exam-save# + +Action logout \ + -next_state done \ + -label #xowf.inclass-exam-submit# \ + -proc activate {obj} { + [[$obj wf_context ] wf_container] addSignature $obj + #set pid [$obj package_id] + #set try_out_mode [$obj property try_out_mode 0] + set return_url [$obj property return_url .] + #:msg "tryout $try_out_mode return_url $return_url" + + #ad_returnredirect $return_url + #ad_script_abort + } + +Action start \ + -next_state working \ + -label #xowf.online-exam-start# \ + -proc activate {obj} { + $obj set_property position 0 + } + +Action start_again \ + -label #xowf.first_question# \ + -next_state working -proc activate {obj} { + $obj set_property position 0 + } + +######################################################################## +# +# State definitions +# +######################################################################## + +State parameter { + {view_method edit} + {extra_js { + urn:ad:js:jquery + ../file:seal.js?m=download + }} + {extra_css { + /resources/xowf/test-item.css + }} +} + +State working \ + -form_loader working_form_loader + +State initial \ + -form_loader working_form_loader + +State done \ + -form_loader done_form_loader + +#-form_loader summary_form + + +######################################################################## +# +# Helper methods for the workflow container +# +######################################################################## + +# +# Field-renaming form loader +# +proc working_form_loader {ctx form_name} { + ns_log notice "============ working_form_loader" + set obj [$ctx object] + set item_nr [$obj property position] + + set parent_id [$obj parent_id] + #:msg "working_form_loader [$obj instance_attributes]" + set parent_obj [::xo::db::CrClass get_instance_from_db -item_id $parent_id] + set parent_state [$parent_obj state] + + # + # In case shuffling is required, fetch via the shuffled position. + # + set shuffle_id [expr {[$parent_obj property shuffle_items 0] ? [$obj creation_user] : -1}] + set position [::xowf::test_item::question_manager shuffled_index \ + -shuffle_id $shuffle_id \ + $parent_obj $item_nr] + ns_log notice "============ working_form_loader load form on pos $position" + # + # Load the form. + # + set form_obj [::xowf::test_item::question_manager nth_question_obj $parent_obj $position] + + # + # Update IP address each time the form is loaded. + # + if {[$obj state] in {"initial" "working"}} { + $obj set_property ip [expr {[ns_conn isconnected] ? [ad_conn peeraddr] : "nowhere"}] + } + + # + # Update the title of the page + # + :set_title $obj -position $position -item_nr $item_nr -for_question -with_minutes + + return $form_obj +} + +# +# Done form loader +# +proc done_form_loader {ctx form_name} { + set obj [$ctx object] + # + # + # + if {[$obj exists __feedback_mode] && [$obj set __feedback_mode] > 0} { + set container [$ctx wf_container] + set result [$container summary_form $ctx $form_name] + } else { + set result [::xowiki::Form new \ + -destroy_on_cleanup \ + -set name en:finished \ + -form {{
+
+

#xowf.inclass-exam-already_answered#

+
+
} text/html} \ + -text {} \ + -anon_instances t \ + ] + } + return $result +} + + +# +# Set "title" with question/user/IP information. Note that the +# "set_title" method is as well responsible for calling the rename +# function via question_manager. +# +:proc set_title { + obj + -position:integer + -item_nr:integer + {-for_question:switch false} + {-with_minutes:switch false} +} { + set parent_obj [::xo::db::CrClass get_instance_from_db -item_id [$obj parent_id]] + if {$for_question && [$obj state] in {initial working}} { + set form_info [::xowf::test_item::question_manager nth_question_form \ + -with_numbers \ + -with_title \ + -with_minutes=$with_minutes \ + -position $position \ + -item_nr $item_nr \ + $parent_obj] + set title_info [lindex [dict get $form_info title_infos] 0] + set titleString [dict get $title_info full_title] + set title [list [string trim $titleString]] + } + lappend title \ + [$parent_obj title] \ + "IP: [$obj property ip]" + #ns_log notice "SETTING $obj title [join $title { · }]" + $obj title [join $title " · "] + + #:msg set_title-set_parameter-MenuBar-[$obj state] + ::xo::cc set_parameter MenuBar 0 + ::xo::cc set_parameter template_file view-plain-master +} + +# +# Form loader for summary (shows all submission data of a user) +# +# This form loader is also called indirectly by www-print-answers of +# oneline-exam.wf +# +:proc summary_form {ctx form_title} { + set obj [$ctx object] + set parent_obj [::xo::db::CrClass get_instance_from_db -item_id [$obj parent_id]] + #:msg "summary_form_loader $form_title [$obj instance_attributes]" + + set shuffle_id [expr {[$parent_obj property shuffle_items 0] ? [$obj creation_user] : -1}] + set form_info [::xowf::test_item::question_manager combined_question_form \ + -with_numbers \ + -with_title \ + -with_minutes \ + -shuffle_id $shuffle_id \ + $parent_obj] + + set summary_form [dict get $form_info form] + set summary_fc [dict get $form_info disabled_form_constraints] + regsub -all {]*>} $summary_form {} summary_form + + :set_title $obj + + return [::xowiki::Form new \ + -destroy_on_cleanup \ + -name en:summary \ + -title $form_title \ + -form [list
$summary_form
text/html] \ + -text {} \ + -anon_instances t \ + -form_constraints $summary_fc] +} + +:proc addSignature {obj} { + set answerAttributes [xowf::test_item::renaming_form_loader \ + answer_attributes [$obj instance_attributes]] + set sha256 [ns_md string -digest sha256 $answerAttributes] + $obj set_property -new true signature $sha256 + return $sha256 +} + + +######################################################################## +# +# Object specific operations +# +######################################################################## + +:object-specific { + # + # Ensure default value is updated for each instance individually. + # + set ctx [:wf_context] + set container [$ctx wf_container] + ${container}::Property ip -default [expr {[ns_conn isconnected] ? [ad_conn peeraddr] : "nowhere"}] + + :log "INCLASS state ${:state}" + set ctx [:wf_context] + set container [$ctx wf_container] + if {$ctx ne $container} { + $ctx forward working_form_loader $container %proc $ctx + $ctx forward done_form_loader $container %proc $ctx + $ctx forward summary_form $container %proc $ctx + } + set :policy ::xowf::test_item::test-item-policy1 + + if {${:state} in {initial working done}} { + set parent_obj [::xo::db::CrClass get_instance_from_db -item_id [:parent_id]] + set question_names [::xowf::test_item::question_manager question_names $parent_obj] + + # + # Use the current_position in the sense of the nth question of the + # user, which is not necessarily the nth question in the list of + # questions due to shuffling. + # + set current_position [:property position] + + set actions {} + #if {$current_position > 0 && ${:state} eq "working"} { + # lappend actions prevQuestion + #} + if {${:state} ne "done"} { + set count 0 + foreach question $question_names { + incr count + ${container}::Action create ${container}::$count \ + -label "$count" \ + -next_state working \ + -extra_css_class [expr {$current_position == $count - 1 ? "current" : ""}] \ + -proc activate {obj} \ + [list :goto_page [expr {$count -1}]] + lappend actions $count + } + if { ${:state} in {initial working} + && [::xowf::test_item::question_manager more_ahead -position $current_position $parent_obj] + } { + lappend actions nextQuestion + } + if {${:state} in {initial working} } { + lappend actions save + } + lappend actions logout + } + + ${container}::${:state} actions $actions + } +} + +# +# Local variables: +# mode: tcl +# tcl-indent-level: 2 +# indent-tabs-mode: nil +# End: Index: openacs-4/packages/xowf/lib/inclass-exam.wf =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/lib/Attic/inclass-exam.wf,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/lib/inclass-exam.wf 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,463 @@ +# -*- Tcl -*- +######################################################################## +# Inclass-Exam workflow, designed similar to online-exam +# ======================================================== +# +# Defining exams: This workflow lets a teacher choose from a +# predefined set of exam questions, which are typically open text, +# short text, single or multiple choice questions. The teacher +# selects test questions via drag and drop. The teacher can perform a +# test run of the created exam, and can get the results via a result +# table. +# +# Publishing and closing exams: When a teacher is satisfied with the +# exam, the exam can be published. In this step, all answers of the +# testing phase are deleted. In the process of publishing, the link to +# start the exam is offered to the user. When the exam is published, +# the teacher can see the incoming answers in the report by refreshing +# the page. When the exam is done, it is unpublished. The workflow +# offers the teacher to see a summary of the results in form of a +# table (an to download the results via csv), or the teacher can +# produce a printer friendly version of the answers. +# +# 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:TestItemText.form} +# {entry -name New.Item.ShortTextInteraction -form en:TestItemShortText.form} +# {entry -name New.Item.MCInteraction -form en:TestItemMC.form} +# {entry -name New.Item.SCInteraction -form en:TestItemSC.form} +# {entry -name New.Item.UploadInteraction -form en:TestItemUpload.form} +# {entry -name New.App.Exam -label "Online Exam" -form en:online-exam.wf} +# +# The policy has to allow the following methods on FormPages: +# +# - "answer" (for students), +# - "edit" (for students), +# - "poll" (for teachers), +# - "print-answers" (for teachers), +# - "print-answer-table" (for teachers), +# - "delete" (for teachers), +# - "qrcode" (for teachers) +# +# Gustaf Neumann, Feb 2012 +######################################################################## +set :autoname 1 ;# to avoid editable name field +set :policy ::xowf::test_item::test-item-policy-publish +set :debug 0 +set :live_updates 0 + +Action select -next_state created -label #xowf.online-exam-select# \ + -title #xowf.online-exam-title-select# +Action publish -next_state published -label #xowf.online-exam-publish# \ + -title #xowf.online-exam-title-publish# +Action unpublish -next_state done -label #xowf.online-exam-unpublish# +Action republish -next_state published -label #xowf.online-exam-republish# \ + -title #xowf.online-exam-title-republish# +Action restart -next_state initial -label #xowf.restart# \ + -title #xowf.online-exam-title-restart# + +State parameter { + {extra_css {/resources/xowf/test-item.css}} +} +State initial -actions {select} -form en:select_question.form -view_method edit +State created -actions {publish restart} -form_loader load_form -view_method edit \ + -form "#xowf.inclass-exam-draft_exam#" +State published -actions {unpublish} -form_loader load_form -view_method edit \ + -form "#xowf.inclass-exam-open#" +State done -actions {republish restart} -form_loader load_form -view_method edit \ + -form "#xowf.inclass-exam-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-exam-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 +} + +######################################################################## +# 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 in the LMS +} + +######################################################################## +# 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 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 "

$title

" + set menu "" + + 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 testrun 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 menu "\[" + if {[acs_user::site_wide_admin_p -user_id [::xo::cc user_id]]} { + append menu "#xowf.online-exam-exam_instances#, " + } + append menu \ + "#xowf.online-exam-protocol#, " \ + "#xowf.online-exam-results-table#\]" + } + } + + switch $state { + "created" - + "done" - + "published" { + # + # In inclass cases, never show all questions on screen, since + # the teacher might have the screen on the projector. + # + template::add_body_script -script urn:ad:js:bootstrap3 + set fullQuestionForm [subst { + +
+ $fullQuestionForm +
+ }] + } + } + + set extraAction "" + switch $state { + "created" { + append extraAction "
" \ + "#xowf.online-exam-try_out# " \ + "#xowf.testrun#" + } + "published" { + append extraAction "
" \ + "#xowf.online-exam-can_answer# " \ + "$aLink" + } + } + + 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 answerStatus [xowf::test_item::answer_manager answers_panel \ + -heading "#xowf.online-exam-submitted_exams_heading#" \ + -submission_msg "#xowf.online-exam-submitted_exams_msg#" \ + -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 {
}] + set total_minutes [::xowf::test_item::question_manager total_minutes $combined_form_info] + set publish_time [::xo::db::tcl_date [$obj last_modified] tz] + set target_time [clock format [expr {[clock scan $publish_time] + $total_minutes*60}] \ + -format %Y-%m-%dT%H:%M:%S] + set countdownHTML [xowf::test_item::answer_manager countdown_timer \ + -target_time $target_time -id "countdown"] + } + + # Remove wrapping forms + regsub -all {]*>} $fullQuestionForm {} fullQuestionForm + + append text [subst { +
+
$answerStatus
+
$fullQuestionForm
+
$qrCode
+
$countdownHTML
+
+ }] + + set footer "$menu $extraAction" + + #-form [subst {
$text
$fullQuestionForm
$report
text/html}] + set f [::xowiki::Form new \ + -destroy_on_cleanup \ + -set name en:question \ + -form [subst {
$text$marked$footer
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 + # + # 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 somewhat printer friendly way. + # + :proc www-print-answer-table {} { + set HTML "" + set ctx [::xowf::Context require [self]] + 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 result_table \ + -package_id ${:package_id} \ + -items $items2 \ + [self]] + $items2 destroy + } + if {$HTML eq ""} { + set HTML "#xowiki.no_data#" + } else { + set HTML "

#xowf.online-exam-results-table#

$HTML" + } + set return_url [[$wf package_id] query_parameter local_return_url:localurl [:pretty_link]] + append HTML "

#xowiki.back#

\n" + + xo::Page requireCSS /resources/xowf/test-item.css + :www-view $HTML + } + + ######################################################################## + # web-callable method "print-answers" + # + # Print the answers in a somewhat printer friendly way. + # + :proc www-print-answers {} { + set HTML "" + 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 $wf] + set withSignature [expr {[dict exists ${:instance_attributes} signature] + ? [dict get ${:instance_attributes} signature] + : 0 }] + set examTitle ${:title} + set filter_id [[$wf package_id] query_parameter id:integer ""] + + 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] + + if {[$i state] ne "done"} { + ns_log notice "online-exam: submission of $userName is not finished (state [$i state])" + continue + } + if {$filter_id ne "" && [$i item_id] ne $filter_id} { + continue + } + + # + # The call to "render_content" calls actually the + # "summary_form" of online-exam-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] + + if {$withSignature} { + set answerAttributes [xowf::test_item::renaming_form_loader \ + answer_attributes [$i instance_attributes]] + set sha256 [ns_md string -digest sha256 $answerAttributes] + set signatureString "
online-exam-actual_signature: $sha256
\n" + set submissionSignature [$i property signature ""] + if {$submissionSignature ne ""} { + append signatureString "
#xowf.online-exam-submission_signature#: $submissionSignature
\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 %T"] + + append HTML "\n
" \ + "

$userName · $fullName · $pretty_date · IP [$i property ip]

" \ + $signatureString \ + $question_form \ + "
\n" + } + } + + if {$HTML eq ""} { + set HTML "#xowiki.no_data#" + } else { + set HTML "

#xowf.online-exam-protocol#

$HTML" + } + set return_url [[$wf package_id] query_parameter local_return_url:localurl [:pretty_link]] + append HTML "

#xowiki.back#

\n" + ::xo::cc set_parameter template_file view-plain-master + ::xo::cc set_parameter MenuBar 0 + xo::Page requireCSS /resources/xowf/test-item.css + :www-view $HTML + } + + ######################################################################## + # web-callable method "answer" + # + # Create or use an answering workflow for the current exam. This is + # a convenience routine to shorten the published URL. + # + :proc www-answer {} { + # + # 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]] + $wf www-create-or-use -parent_id [:item_id] + } + } + + :proc www-qrcode {} { + 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" + # + # Return statistics about working and finished exams. + # + :proc www-poll {} { + 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 + } +} + +# +# Local variables: +# mode: tcl +# tcl-indent-level: 2 +# indent-tabs-mode: nil +# End: Index: openacs-4/packages/xowf/lib/inclass-quiz-answer.wf =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/lib/Attic/inclass-quiz-answer.wf,v diff -u -N -r1.1.2.5 -r1.1.2.6 --- openacs-4/packages/xowf/lib/inclass-quiz-answer.wf 5 Dec 2019 21:09:53 -0000 1.1.2.5 +++ openacs-4/packages/xowf/lib/inclass-quiz-answer.wf 21 Feb 2020 13:45:15 -0000 1.1.2.6 @@ -88,11 +88,6 @@ set more_ahead 1 set quiz_available 1 - #if {$parent_state eq "created" && [$obj property try_out_mode]} { - # ns_log notice "FAKE state as published" - # set parent_state "published" - #} - switch $parent_state { "published" { # @@ -105,7 +100,7 @@ set form_answer [xowf::test_item::renaming_form_loader answer_for_form \ $form_name \ [$obj instance_attributes]] - ns_log notice "CURRENT answer '$form_answer' form_name $form_name" + #ns_log notice "CURRENT answer '$form_answer' form_name $form_name" if {$form_answer eq ""} { # # It was not answered yet, show the question as 'waiting_form' Index: openacs-4/packages/xowf/lib/inclass-quiz.wf =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/lib/Attic/inclass-quiz.wf,v diff -u -N -r1.1.2.10 -r1.1.2.11 --- openacs-4/packages/xowf/lib/inclass-quiz.wf 5 Feb 2020 20:48:42 -0000 1.1.2.10 +++ openacs-4/packages/xowf/lib/inclass-quiz.wf 21 Feb 2020 13:45:15 -0000 1.1.2.11 @@ -138,10 +138,11 @@ :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 current_form_info [::xowf::test_item::question_manager combined_question_form \ + set combined_form_info [::xowf::test_item::question_manager combined_question_form \ -with_numbers $obj] } default { @@ -151,13 +152,12 @@ "results" {set title "#xowf.results_of#: $title"} } set current_question [xowf::test_item::question_manager current_question_obj $obj] - set current_form_info [::xowf::test_item::question_manager current_question_form $obj] + set combined_form_info [::xowf::test_item::question_manager current_question_form $obj] } } - set fullQuestionForm [dict get $current_form_info form] - set full_fc [dict get $current_form_info disabled_form_constraints] - set wf [xowf::test_item::answer_manager get_answer_wf $obj] + set fullQuestionForm [dict get $combined_form_info form] + set full_fc [dict get $combined_form_info disabled_form_constraints] set qrCode "" set answerStatus "" @@ -174,7 +174,7 @@ "published" { set src [$obj pretty_link -query m=qrcode] - set qrCode [subst {
}] + set qrCode [subst {
}] set answerStatus [xowf::test_item::answer_manager answers_panel \ -polling=${:live_updates} \ -manager_ob $obj \ @@ -185,12 +185,13 @@ "done" - "results" { [$ctx object] setCSSDefaults - set marked [xowf::test_item::answer_manager marked_results $wf $current_form_info] + set marked [xowf::test_item::answer_manager marked_results -obj $obj -wf $wf $combined_form_info] set answerStatus [xowf::test_item::answer_manager answers_panel \ -manager_ob $obj \ -wf $wf \ -current_question $current_question] } + default { :msg "not handled: state=$state" } Index: openacs-4/packages/xowf/lib/online-exam-answer.wf =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/lib/online-exam-answer.wf,v diff -u -N -r1.2.2.21 -r1.2.2.22 --- openacs-4/packages/xowf/lib/online-exam-answer.wf 3 Feb 2020 22:55:55 -0000 1.2.2.21 +++ openacs-4/packages/xowf/lib/online-exam-answer.wf 21 Feb 2020 13:45:15 -0000 1.2.2.22 @@ -56,18 +56,17 @@ # or closed it already). Only allow usage in the try-out-mode. # if {$parent_state ne "published" && [$obj property try_out_mode 0] == 0} { - #:msg "LOCKED" set current_state [$obj property _state] - set lockin_state [expr {$current_state eq "initial" ? "initial" : "done"}] - set lockin_msg(initial) "#xowf.online-exam-not-published#" - set lockin_msg(done) "#xowf.online-exam-finished#" + set locking_state [expr {$current_state eq "initial" ? "initial" : "done"}] + set locking_msg(initial) "#xowf.online-exam-not-published#" + set locking_msg(done) "#xowf.online-exam-finished#" - util_user_message -message $lockin_msg($lockin_state) + util_user_message -message $locking_msg($locking_state) # # Force the user in the done state. Alternatively, we could # handle this in the provide a different form or push the user to some other state. # - [:wf_context] set_current_state $lockin_state + [:wf_context] set_current_state $locking_state } else { #:msg "not LOCKED" @@ -114,7 +113,6 @@ -label #xowf.online-exam-submit# \ -proc activate {obj} { [[$obj wf_context ] wf_container] addSignature $obj - set pid [$obj package_id] set try_out_mode [$obj property try_out_mode 0] set return_url [$obj property return_url .] #:msg "tryout $try_out_mode return_url $return_url" @@ -214,7 +212,9 @@ } # -# Set "title" with question/user/IP information. +# Set "title" with question/user/IP information. Note that the +# "set_title" method is as well responsible for calling the rename +# function via question_manager. # :proc set_title { obj @@ -232,7 +232,8 @@ -position $position \ -item_nr $item_nr \ $parent_obj] - set titleString [dict get $form_info title_infos full_title] + set title_info [lindex [dict get $form_info title_infos] 0] + set titleString [dict get $title_info full_title] set title [list [string trim $titleString]] } lappend title \ Index: openacs-4/packages/xowf/lib/online-exam.wf =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/lib/online-exam.wf,v diff -u -N -r1.6.2.21 -r1.6.2.22 --- openacs-4/packages/xowf/lib/online-exam.wf 15 Feb 2020 10:37:53 -0000 1.6.2.21 +++ openacs-4/packages/xowf/lib/online-exam.wf 21 Feb 2020 13:45:15 -0000 1.6.2.22 @@ -148,23 +148,32 @@ set pLink "." set menu "" } else { + # + # Always compute the testrun 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 lLink "$wf_pretty_link?m=list" set aLink [$obj pretty_link -query m=answer] - set pLink1 [$obj pretty_link -query m=print-answers] - set pLink2 [$obj pretty_link -query m=print-answer-table] - #util_user_message -html -message "$survey is available as $pLink" - # [#xowf.refresh#, - set menu "\[" - if {[acs_user::site_wide_admin_p -user_id [::xo::cc user_id]]} { - append menu "#xowf.online-exam-exam_instances#, " + # + # 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 menu "\[" + if {[acs_user::site_wide_admin_p -user_id [::xo::cc user_id]]} { + append menu "#xowf.online-exam-exam_instances#, " + } + append menu \ + "#xowf.online-exam-protocol#, " \ + "#xowf.online-exam-results-table#\]" } - append menu \ - "#xowf.online-exam-protocol#, " \ - "#xowf.online-exam-results-table#\]" } set extraAction "" @@ -184,7 +193,7 @@ if {$state in {published done}} { if {$state eq "done"} { [$ctx object] setCSSDefaults - set marked [xowf::test_item::answer_manager marked_results $wf $combined_form_info] + set marked [xowf::test_item::answer_manager marked_results -obj $obj -wf $wf $combined_form_info] } set answerStats [xowf::test_item::answer_manager answers_panel \ -heading "#xowf.online-exam-submitted_exams_heading#" \ Index: openacs-4/packages/xowf/tcl/test-item-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/tcl/test-item-procs.tcl,v diff -u -N -r1.7.2.25 -r1.7.2.26 --- openacs-4/packages/xowf/tcl/test-item-procs.tcl 12 Feb 2020 12:43:26 -0000 1.7.2.25 +++ openacs-4/packages/xowf/tcl/test-item-procs.tcl 21 Feb 2020 13:45:15 -0000 1.7.2.26 @@ -149,7 +149,7 @@ set auto_correct ${:auto_correct} set can_shuffle true } - ul { # + ul { # set interaction_class upload_interaction set options "" set auto_correct false @@ -854,11 +854,55 @@ #:msg "fc=$fc" } } +############################################################################ +# Generic Assement interface +############################################################################ namespace eval ::xowf::test_item { - nx::Object create renaming_form_loader { + nx::Class create AssessmentInterface { # + # Abstract class for common functionality + # + :method assert_assessment_container {o:object} { + set ok [expr {[$o is_wf_instance] == 0 && [$o is_wf] == 1}] + if {!$ok} { + ns_log notice "NO ASSESSMENT CONTAINER [$o title]" + ns_log notice "NO ASSESSMENT CONTAINER page_template [[$o page_template] title]" + ns_log notice "NO ASSESSMENT CONTAINER iswfi [$o is_wf_instance] iswf [$o is_wf]" + ns_log notice "[$o serialize]" + error "'[lindex [info level -1] 0]': not an assessment container" + } + } + + :method assert_assessment {o:object} { + if {[llength [$o property question]] == 0} { + ns_log notice "NO ASSESSMENT [$o title]" + ns_log notice "NO ASSESSMENT page_template [[$o page_template] title]" + ns_log notice "NO ASSESSMENT iswfi [$o is_wf_instance] iswf [$o is_wf]" + ns_log notice "[$o serialize]" + error "'[lindex [info level -1] 0]': object has no questions" + } + } + + :method assert_answer_instance {o:object} { + # we could include as well {[$o property answer] ne ""} in case we initialize it + set ok [expr {[$o is_wf_instance] == 1 && [$o is_wf] == 0}] + if {!$ok} { + ns_log notice "NO ANSWER [$o title]" + ns_log notice "NO ANSWER page_template [[$o page_template] title]" + ns_log notice "NO ANSWER iswfi [$o is_wf_instance] iswf [$o is_wf]" + ns_log notice "[$o serialize]" + error "'[lindex [info level -1] 0]': not an answer instance" + } + } + + } +} +namespace eval ::xowf::test_item { + + nx::Class create Renaming_form_loader -superclass AssessmentInterface { + # # Form loader that renames "generic" form-field-names as provided # by the test-item form-field classes (@answer@) into names based # on the form name, such that multiple of these form names can be @@ -873,7 +917,7 @@ # - rename_attributes # - :object method map_form_constraints {form_constraints oldName newName} { + :method map_form_constraints {form_constraints oldName newName} { # # Rename form constraints starting with $oldName into $newName. # Handle as well "answer=$oldName" form constraint properties. @@ -891,7 +935,7 @@ }] } - :public object method form_name_based_attribute_stem {formName} { + :public method form_name_based_attribute_stem {formName} { # # Produce from the provided 'formName' an attribute stem for the # input fields of this form. @@ -902,7 +946,7 @@ } - :public object method answer_attributes {instance_attributes} { + :public method answer_attributes {instance_attributes} { # # Return all form-loader specific attributes from # instance_attributes. @@ -916,7 +960,7 @@ return $result } - :public object method answer_for_form {formName instance_attributes} { + :public method answer_for_form {formName instance_attributes} { # # Return answer for the provided formName from # instance_attributes of a single object. @@ -934,7 +978,7 @@ return $result } - :public object method answers_for_form {formName answers} { + :public method answers_for_form {formName answers} { # # Return a list of dicts for the provided formName from the # answers (as returned from [answer_manager get_answers ...]). @@ -954,7 +998,7 @@ return $result } - :public object method rename_attributes {form_obj:object} { + :public method rename_attributes {form_obj:object} { set form [$form_obj get_property -name form] set fc [$form_obj get_property -name form_constraints] @@ -984,20 +1028,22 @@ return $form_obj } - :public object method get_form_object {{-set_title:boolean true} ctx:object form_name} { - #ns_log notice "renaming_form_loader get_form_object for form_name <$form_name>" + :public method get_form_object {{-set_title:boolean true} ctx:object form_name} { set form_id [$ctx default_load_form_id $form_name] set obj [$ctx object] set form_obj [::xo::db::CrClass get_instance_from_db -item_id $form_id] return [:rename_attributes $form_obj] } } + + Renaming_form_loader create renaming_form_loader } + namespace eval ::xowf::test_item { - nx::Object create answer_manager { + nx::Class create Answer_manager -superclass AssessmentInterface { # # Public API: @@ -1010,10 +1056,10 @@ # # - marked_results # - answers_panel - # - participant_result # - result_table # - :public object method create_workflow { + + :public method create_workflow { {-answer_workflow /packages/xowf/lib/online-exam-answer.wf} {-master_workflow en:Workflow.form} parentObj:object @@ -1090,7 +1136,7 @@ ######################################################################## - :public object method delete_all_answer_data {obj:object} { + :public method delete_all_answer_data {obj:object} { # # Delete all instances of the answer workflow # @@ -1105,7 +1151,7 @@ ######################################################################## - :public object method get_answer_wf {obj:object} { + :public method get_answer_wf {obj:object} { # # return the workflow denoted by the property wfName in obj # @@ -1118,12 +1164,14 @@ ######################################################################## - :public object method get_wf_instances { + :public method get_wf_instances { {-initialize false} {-orderby ""} wf:object } { # get_wf_instances: return the workflow instances + :assert_assessment_container $wf + return [::xowiki::FormPage get_form_entries \ -base_item_ids [$wf item_id] \ -form_fields "" \ @@ -1136,7 +1184,7 @@ ######################################################################## - :public object method get_answers {{-state ""} wf:object} { + :public method get_answers {{-state ""} wf:object} { set results {} set items [:get_wf_instances $wf] foreach i [$items children] { @@ -1152,24 +1200,26 @@ ######################################################################## - :object method participant_result {obj:object form_info} { - # - # In case, the passed-in obj modifies during rendering the - # perconnection parameters, save and restore these. - # - set form_fields [:answer_form_field_objs -wf $obj] - $obj form_field_index $form_fields + :method participant_result { + -obj:object + answerObj:object + form_info + form_field_objs + } { - set instance_attributes [$obj instance_attributes] - set answer [list item $obj] + :assert_answer_instance $answerObj + :assert_assessment $obj - foreach f $form_fields { + set instance_attributes [$answerObj instance_attributes] + set answer [list item $answerObj] + + foreach f $form_field_objs { set att [$f name] if {[dict exists $instance_attributes $att]} { set value [dict get $instance_attributes $att] #ns_log notice "### '$att' value '$value'" - $obj combine_data_and_form_field_default 1 $f $value + $answerObj combine_data_and_form_field_default 1 $f $value $f set_feedback 1 $f add_statistics -options {word_statistics word_cloud} # @@ -1189,7 +1239,7 @@ return $answer } - :object method answer_form_field_objs {-clear:switch -wf:object} { + :method answer_form_field_objs {-clear:switch -wf:object form_info} { set key ::__test_item_answer_form_fields if {$clear} { # @@ -1199,25 +1249,32 @@ # unset $key } else { + #ns_log notice "### key exists [info exists $key]" if {![info exists $key]} { - set form_info [::xowf::test_item::question_manager combined_question_form -with_numbers $wf] + #ns_log notice "form_info: $form_info" + set fc [dict get $form_info disabled_form_constraints] set pc_params [::xo::cc perconnection_parameter_get_all] + #ns_log notice "### create_form_fields_from_form_constraints <$fc>" set $key [$wf create_form_fields_from_form_constraints \ -lookup \ - [dict get $form_info disabled_form_constraints]] + [lsort -unique $fc]] ::xo::cc perconnection_parameter_set_all $pc_params + $wf form_field_index [set $key] } return [set $key] } } - :public object method result_table { + + :public method result_table { -package_id:integer -items:object,required {-view_all_method print-answers} wf:object } { - set answer_form_field_objs [:answer_form_field_objs -wf $wf] + #set form_info [:combined_question_form -with_numbers $wf] + set form_info [::xowf::test_item::question_manager combined_question_form $wf] + set answer_form_field_objs [:answer_form_field_objs -wf $wf $form_info] set form_field_objs [$wf create_raw_form_field \ -name _online-exam-userName \ @@ -1302,17 +1359,21 @@ return $HTML } - :public object method marked_results {wf:object form_info} { + :public method marked_results {-obj:object -wf:object form_info} { + set form_field_objs [:answer_form_field_objs -wf $wf $form_info] + set items [:get_wf_instances $wf] set results "" foreach i [$items children] { - set participantResult [:participant_result $i $form_info] + set participantResult [:participant_result -obj $obj $i $form_info $form_field_objs] append results $participantResult \n } + + #ns_log notice "=== marked_results of [llength [$items children]] items => $results" return $results } - :public object method answers_panel { + :public method answers_panel { {-polling:switch false} {-heading #xowf.submitted_answers#} {-submission_msg #xowf.participants_answered_question#} @@ -1372,28 +1433,60 @@ return $answerStatus } - } -} -namespace eval ::xowf::test_item { - ::xotcl::Class create ::xowf::test_item::td_pretty_value \ - -superclass ::xowiki::formfield::FormField + :public method countdown_timer { + {-target_time:required} + {-id:required} + } { + # new Date('1995-12-17T03:24:00') + template::add_body_script -script [subst { + var countdown_target_date = new Date('$target_time').getTime(); + var countdown_days, countdown_hours, countdown_minutes, countdown_seconds; + var countdown = document.getElementById('$id'); - ::xowf::test_item::td_pretty_value instproc pretty_value {value} { - #ns_log notice "${:name} pretty_value [:info precedence]" - if {"::xowiki::formfield::checkbox" in [:info precedence]} { - set v ${value} - } else { - set v [next] + setInterval(function () { + var current_date = new Date().getTime(); + var seconds_left = (countdown_target_date - current_date) / 1000; + var HTML = ''; + + countdown_days = parseInt(seconds_left / 86400); + seconds_left = seconds_left % 86400; + countdown_hours = parseInt(seconds_left / 3600); + seconds_left = seconds_left % 3600; + countdown_minutes = parseInt(seconds_left / 60); + countdown_seconds = parseInt(seconds_left % 60); + + if (countdown_days != 0) { + HTML += '' + countdown_days + ' ' + + (countdown_days != 1 ? '[_ xowf.Days]' : '[_ xowf.Day]') + + ' '; + } + if (countdown_hours != 0 || countdown_days != 0) { + HTML += '' + countdown_hours + ' ' + + (countdown_hours != 1 ? '[_ xowf.Hours]' : '[_ xowf.Hour]') + + ' '; + } + HTML += '' + countdown_minutes + ' ' + + (countdown_minutes != 1 ? '[_ xowf.Minutes]' : '[_ xowf.Minute]') + + ' ' + + '' + countdown_seconds + ' ' + + (countdown_seconds != 1 ? '[_ xowf.Seconds]' : '[_ xowf.Second]') + + ' [_ xowf.remaining]' ; + + countdown.innerHTML = HTML; + }, 1000); + }] + return "
" } - return $v } + + Answer_manager create answer_manager } + namespace eval ::xowf::test_item { - - nx::Object create question_manager { + nx::Class create Question_manager -superclass AssessmentInterface { # # This code manages questions and the information related to a # current (selected) question via qthe "position" instance @@ -1413,20 +1506,21 @@ # - question_objs # - question_names # - question_property + # - total_minutes # - :public object method goto_page {obj:object position} { + :public method goto_page {obj:object position} { $obj set_property position $position } - :public object method more_ahead {{-position ""} obj:object} { + :public method more_ahead {{-position ""} obj:object} { if {$position eq ""} { set position [$obj property position] } set questions [dict get [$obj instance_attributes] question] return [expr {$position + 1 < [llength $questions]}] } - :object method load_question_objs {obj names} { + :method load_question_objs {obj:object names} { set questions [lmap ref $names { if {![string match "*/*" $ref]} { set ref [[$obj parent_id] name]/$ref @@ -1438,21 +1532,22 @@ -package_id [$obj package_id] \ -default_lang [$obj lang] \ -forms $questionNames] + #ns_log notice "load_question_objs called with $obj $names -> $questionForms" return $questionForms } - :public object method current_question_name {obj:object} { + :public method current_question_name {obj:object} { set questions [dict get [$obj instance_attributes] question] return [lindex [dict get [$obj instance_attributes] question] [$obj property position]] } - :public object method current_question_obj {obj:object} { + :public method current_question_obj {obj:object} { return [:load_question_objs $obj [:current_question_name $obj]] } - :public object method shuffled_question_objs {obj:object shuffle_id} { + :public method shuffled_question_objs {obj:object shuffle_id} { set form_objs [:question_objs $obj] set result {} foreach i [::xowiki::randomized_indices -seed $shuffle_id [llength $form_objs]] { @@ -1461,7 +1556,7 @@ return $result } - :public object method shuffled_index {{-shuffle_id:integer -1} obj:object position} { + :public method shuffled_index {{-shuffle_id:integer -1} obj:object position} { if {$shuffle_id > -1} { set form_objs [:question_objs $obj] set shuffled [::xowiki::randomized_indices -seed $shuffle_id [llength $form_objs]] @@ -1470,7 +1565,8 @@ return $position } - :public object method question_objs {{-shuffle_id:integer -1} obj:object} { + :public method question_objs {{-shuffle_id:integer -1} obj:object} { + :assert_assessment $obj set form_objs [:load_question_objs $obj [$obj property question]] if {$shuffle_id > -1} { set result {} @@ -1482,18 +1578,18 @@ return $form_objs } - :public object method question_names {obj:object} { + :public method question_names {obj:object} { return [$obj property question] } - :public object method nth_question_obj {obj:object position:integer} { + :public method nth_question_obj {obj:object position:integer} { + :assert_assessment $obj set questions [dict get [$obj instance_attributes] question] set result [:load_question_objs $obj [lindex $questions $position]] - #ns_log notice "nth_question_obj called with $position -> $result" return $result } - :object method question_info { + :method question_info { {-numbers ""} {-with_title:switch false} {-with_minutes:switch false} @@ -1503,6 +1599,7 @@ set full_fc {} set full_disabled_fc {} set title_infos {} + foreach form_obj $form_objs number $numbers { set form_obj [::xowf::test_item::renaming_form_loader rename_attributes $form_obj] set form_title [$form_obj title] @@ -1520,11 +1617,10 @@ append full_form "

$title

\n" append full_form [$form_obj property form] \n - lappend title_infos \ - full_title $title \ - title $form_title \ - minutes $minutes \ - number $number + lappend title_infos [list full_title $title \ + title $form_title \ + minutes $minutes \ + number $number] lappend full_fc [$form_obj property form_constraints] lappend full_disabled_fc [$form_obj property disabled_form_constraints] } @@ -1536,7 +1632,7 @@ } - :public object method question_property {form_obj:object attribute {default ""}} { + :public method question_property {form_obj:object attribute {default ""}} { # # Get an attribute of the original question # @@ -1550,7 +1646,7 @@ return $value } - :public object method minutes_string {form_obj:object} { + :public method minutes_string {form_obj:object} { # # Get an attribute of the original question # @@ -1561,7 +1657,7 @@ } } - :public object method combined_question_form { + :public method combined_question_form { {-with_numbers:switch false} {-with_title:switch false} {-with_minutes:switch false} @@ -1585,15 +1681,25 @@ $form_objs] } - :public object method current_question_form { + :public method total_minutes {form_info} { + set minutes 0 + foreach title_info [dict get $form_info title_infos] { + if {[dict exists $title_info minutes]} { + set minutes [expr {$minutes + [dict get $title_info minutes]}] + } + } + return $minutes + } + + :public method current_question_form { {-with_numbers:switch false} {-with_title:switch false} obj:object } { return [:nth_question_form -with_numbers=$with_numbers -with_title=$with_title $obj] } - :public object method nth_question_form { + :public method nth_question_form { {-position:integer} {-item_nr:integer} {-with_numbers:switch false} @@ -1621,33 +1727,40 @@ $form_objs] } - :public object method current_question_number {obj:object} { + :public method current_question_number {obj:object} { return [expr {[$obj property position] + 1}] } - :public object method current_question_title {{-with_numbers:switch false} obj:object} { + :public method current_question_title {{-with_numbers:switch false} obj:object} { if {$with_numbers} { return "#xowf.question# [:current_question_number $obj]" } } + } + Question_manager create question_manager - # :public object method set_page {obj increment} { - # #set pages [$obj property pages] - # set position [$obj property position 0] - # incr position $increment - # if {$position < 0} { - # set position 0 - # } elseif {$position >= [llength $pages]} { - # set position [expr {[llength $pages] - 1}] - # } - # $obj set_property position $position - # #$obj set_property -new 1 current_form [lindex $pages $position] - # } - } } namespace eval ::xowf::test_item { + # + # Define handling of form-field "td_pretty_value" + # + ::xotcl::Class create ::xowf::test_item::td_pretty_value \ + -superclass ::xowiki::formfield::FormField + ::xowf::test_item::td_pretty_value instproc pretty_value {value} { + #ns_log notice "${:name} pretty_value [:info precedence]" + if {"::xowiki::formfield::checkbox" in [:info precedence]} { + set v ${value} + } else { + set v [next] + } + return $v + } +} + + +namespace eval ::xowf::test_item { # # Copy the default policy (policy1) from xowiki and add elements for # FormPages as needed by the demo workflows: Index: openacs-4/packages/xowf/tcl/xowf-init.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/tcl/xowf-init.tcl,v diff -u -N -r1.3.2.3 -r1.3.2.4 --- openacs-4/packages/xowf/tcl/xowf-init.tcl 14 Feb 2020 13:42:12 -0000 1.3.2.3 +++ openacs-4/packages/xowf/tcl/xowf-init.tcl 21 Feb 2020 13:45:15 -0000 1.3.2.4 @@ -4,6 +4,11 @@ ::xowf::dav-todo register # +# Make sure, the site-wide pages are loaded +# +::xowf::Package require_site_wide_pages + +# # Run the checker for the scheduled at-jobs. # # As we are trying to run as close as possible to the minute change, Index: openacs-4/packages/xowf/tcl/xowf-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/tcl/xowf-procs.tcl,v diff -u -N -r1.28.2.25 -r1.28.2.26 --- openacs-4/packages/xowf/tcl/xowf-procs.tcl 14 Feb 2020 13:42:12 -0000 1.28.2.25 +++ openacs-4/packages/xowf/tcl/xowf-procs.tcl 21 Feb 2020 13:45:15 -0000 1.28.2.26 @@ -16,6 +16,7 @@ ::xo::db::require package xowiki ::xo::library require -package xowiki xowiki-procs +::xo::library require -package xotcl-core 06-package-procs namespace eval ::xowf { # @@ -44,6 +45,23 @@ parameter_page en:xowf-site-wide-parameter } + Package site_wide_pages { + Workflow.form + + TestItemText.form + TestItemShortText.form + TestItemMC.form + TestItemSC.form + TestItemUpload.form + + online-exam.wf + inclass-quiz.wf + inclass-exam.wf + + quiz-select_question.form + select_question.form + } + Package default_package_parameters { parameter_page en:xowf-default-parameter } @@ -54,11 +72,46 @@ instance_attributes { MenuBar t top_includelet none production_mode t with_user_tracking t with_general_comments f with_digg f with_tags f - ExtraMenuEntries {{entry -name New.Extra.Workflow -form /en:Workflow.form}} + ExtraMenuEntries {{entry -name New.Extra.Workflow -form en:Workflow.form}} with_delicious f with_notifications f security_policy ::xowiki::policy1 } } + Package ad_proc create_new_workflow_page { + -package_id:required + -parent_id:required + -name:required + -title:required + {-instance_attributes ""} + } { + Helper proc for loading workflow prototype page with less effort. + } { + # + # Load Workflow.form + # + xo::Package require $package_id + set item_ref_info [$package_id item_ref -use_site_wide_pages true -default_lang en \ + -parent_id $parent_id \ + en:Workflow.form] + set page_template [dict get $item_ref_info item_id] + if {$page_template != 0} { + # + # Create FormPage + # + set p [::xowiki::FormPage new \ + -name $name \ + -title $title \ + -set text {} \ + -instance_attributes $instance_attributes \ + -page_template $page_template] + } else { + ns_log error "could not load Workflow form, therefore creation of workflow $name failed as well" + set p "" + } + return $p + } + + Package ad_instproc initialize {} { Add mixin ::xowf::WorkflowPage to every FormPage. } { @@ -99,6 +152,8 @@ next } + + # Package instproc delete {-item_id -name} { # # Provide a method to delete the foreign key references, when # # an item for an atjob is deleted. We do here the same magic Index: openacs-4/packages/xowf/www/prototypes/TestItemMC.form.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/prototypes/Attic/TestItemMC.form.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/www/prototypes/TestItemMC.form.page 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,12 @@ +# -*- tcl-*- +::xowiki::Form new \ + -name en:TestItemMC.form \ + -title "MC Item" \ + -anon_instances f \ + -text {} \ + -form {{
@question@ @_nls_language@
} text/html} \ + -form_constraints { + {question:test_item,question_type=mc2,grading=ggw wi1 wi2 exact,feedback_level=single,label=#xowf.mc_question#} + _description:omit _page_order:omit + } + Index: openacs-4/packages/xowf/www/prototypes/TestItemSC.form.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/prototypes/Attic/TestItemSC.form.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/www/prototypes/TestItemSC.form.page 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,14 @@ +# -*- tcl-*- +::xowiki::Form new \ + -name en:TestItemSC.form \ + -title "SC Item" \ + -anon_instances f \ + -text {} \ + -form {{
@question@ @_nls_language@
} text/html} \ + -form_constraints { + question:test_item,question_type=sc,grading=exact,feedback_level=single,label=#xowf.sc_question# + _description:omit _page_order:omit + } + + + Index: openacs-4/packages/xowf/www/prototypes/TestItemShortText.form.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/prototypes/Attic/TestItemShortText.form.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/www/prototypes/TestItemShortText.form.page 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,14 @@ +# -*- tcl-*- +::xowiki::Form new \ + -name en:TestItemShortText.form \ + -title "ShortTextItem" \ + -anon_instances f \ + -text {} \ + -form {{
@question@ @_nls_language@
} text/html} \ + -form_constraints { + question:test_item,question_type=st,grading=none,feedback_level=single,auto_correct=1,label=#xowf.short_text_question# + _description:omit _page_order:omit + } + + + Index: openacs-4/packages/xowf/www/prototypes/TestItemText.form.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/prototypes/Attic/TestItemText.form.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/www/prototypes/TestItemText.form.page 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,14 @@ +# -*- tcl-*- +::xowiki::Form new \ + -name en:TestItemText.form \ + -title "TextItem" \ + -anon_instances f \ + -form {{
@question@ @_nls_language@
} text/html} \ + -form_constraints { +question:test_item,question_type=ot,grading=none,feedback_level=single,auto_correct=1,label=#xowf.text_question# +_description:omit +_page_order:omit +} + + + Index: openacs-4/packages/xowf/www/prototypes/TestItemUpload.form.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/prototypes/Attic/TestItemUpload.form.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/www/prototypes/TestItemUpload.form.page 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,14 @@ +# -*- tcl-*- +::xowiki::Form new \ + -name en:TestItemUpload.form \ + -title "File Submission Item" \ + -anon_instances f \ + -text {} \ + -form {{
@question@ @_nls_language@
} text/html} \ + -form_constraints { + {question:test_item,question_type=ul,feedback_level=single,label=#xowf.ul_question#} + _description:omit _page_order:omit + } + + + Index: openacs-4/packages/xowf/www/prototypes/assessment-index.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/prototypes/Attic/assessment-index.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/www/prototypes/assessment-index.page 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,17 @@ +::xowiki::Page new -title "Assessment Index" -text { +

#xowf.Created_Questions#:

+ +

{{form-usages -title "Questions" -form en:TestItemText.form|en:TestItemShortText.form|en:TestItemMC.form|en:TestItemSC.form|en:TestItemUpload.form -buttons "edit duplicate slim_publish_status delete" -field_names "_title,_last_modified,_creation_user" -date_format "pretty-age" -csv false}}

+ +

#xowf.Created_Assessments#:

+ +

{{form-usages -title "Online Exams" -form en:online-exam.wf|en:inclass-quiz.wf|en:inclass-exam.wf -buttons "edit delete" -field_names "_title,_state,_last_modified,_creation_user" -date_format "pretty-age" -csv false}}
+
+{{set-parameter __no_footer 1}}

+ + +} + + + + Index: openacs-4/packages/xowf/www/prototypes/inclass-exam.wf.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/prototypes/Attic/inclass-exam.wf.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/www/prototypes/inclass-exam.wf.page 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,16 @@ +# -*- tcl-*- +# The variable package_id and parent_id are provided via the caller context +xowf::Package create_new_workflow_page \ + -package_id $package_id \ + -parent_id $parent_id \ + -name en:inclass-exam.wf \ + -title "In-class Exam Workflow" \ + -instance_attributes { + workflow_definition {::xowf::include /packages/xowf/lib/inclass-exam.wf} + form_constraints {} + return_url {} + } + + + + Index: openacs-4/packages/xowf/www/prototypes/inclass-quiz.wf.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/prototypes/Attic/inclass-quiz.wf.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/www/prototypes/inclass-quiz.wf.page 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,16 @@ +# -*- tcl-*- +# The variable package_id and parent_id are provided via the caller context +xowf::Package create_new_workflow_page \ + -package_id $package_id \ + -parent_id $parent_id \ + -name en:inclass-quiz.wf \ + -title "In-class Quiz Workflow" \ + -instance_attributes { + workflow_definition {::xowf::include /packages/xowf/lib/inclass-quiz.wf} + form_constraints {} + return_url {} + } + + + + Index: openacs-4/packages/xowf/www/prototypes/online-exam.wf.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/prototypes/Attic/online-exam.wf.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/www/prototypes/online-exam.wf.page 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,12 @@ +# -*- tcl-*- +# The variable package_id and parent_id are provided via the caller context +xowf::Package create_new_workflow_page \ + -package_id $package_id \ + -parent_id $parent_id \ + -name en:online-exam.wf \ + -title "Online Exam Workflow" \ + -instance_attributes { + workflow_definition {::xowf::include /packages/xowf/lib/online-exam.wf} + form_constraints {} + return_url {} + } Index: openacs-4/packages/xowf/www/prototypes/quiz-select_question.form.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/prototypes/Attic/quiz-select_question.form.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/www/prototypes/quiz-select_question.form.page 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,14 @@ +# -*- tcl-*- +::xowiki::Form new \ + -name quiz-select_question.form \ + -title "Select Question" \ + -anon_instances f \ + -text {} \ + -form {{
@question@ @_nls_language@
} text/html} \ + -form_constraints { + @cr_fields:hidden + {_title:text,label=#xowf.inclass-quiz-name#,default=#xowf.inclass-quiz-default_name#} + {question:form_page,multiple=true,form=en:TestItemText.form|en:TestItemShortText.form|en:TestItemMC.form|en:TestItemSC.form,required,help_text=#xowf.select_question_help_text#,label=#xowiki.questions#} + _description:omit _page_order:omit + } + Index: openacs-4/packages/xowf/www/prototypes/select_question.form.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/prototypes/Attic/select_question.form.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/www/prototypes/select_question.form.page 21 Feb 2020 13:45:15 -0000 1.1.2.1 @@ -0,0 +1,16 @@ +# -*- tcl-*- +::xowiki::Form new \ + -name select_question.form \ + -title "Select Question" \ + -anon_instances f \ + -text {} \ + -form {{
@question@ @_nls_language@
} text/html} \ + -form_constraints { + @cr_fields:hidden + {_title:text,label=#xowf.online-exam-name#,default=#xowf.online-exam-default_name#} + {question:form_page,multiple=true,form=en:TestItemText.form|en:TestItemShortText.form|en:TestItemMC.form|en:TestItemSC.form|en:TestItemUpload.form,required,help_text=#xowf.select_question_help_text#,label=#xowiki.questions#} + {shuffle_items:boolean,horizontal=true,label=#xowf.randomized_items#,help_text=#xowf.randomized_items_help_text#} + {signatur:boolean,horizontal=true,default=f,label=#xowf.signature#,help_text=#xowf.signature-help_text#} + _description:omit _page_order:omit + } + Index: openacs-4/packages/xowf/www/resources/test-item.css =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/www/resources/Attic/test-item.css,v diff -u -N -r1.1.2.12 -r1.1.2.13 --- openacs-4/packages/xowf/www/resources/test-item.css 11 Feb 2020 21:26:04 -0000 1.1.2.12 +++ openacs-4/packages/xowf/www/resources/test-item.css 21 Feb 2020 13:45:15 -0000 1.1.2.13 @@ -192,3 +192,8 @@ height: auto; /* let the content decide it */ } + +div.xowiki-content h1 { + font-size: 24px; +} +