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.73 -r1.12.2.74 --- openacs-4/packages/xowf/xowf.info 28 Jan 2022 12:51:11 -0000 1.12.2.73 +++ openacs-4/packages/xowf/xowf.info 31 Jan 2022 13:15:42 -0000 1.12.2.74 @@ -10,16 +10,16 @@ t xowf - + Gustaf Neumann XoWiki Content Flow - an XoWiki based workflow system implementing state-based behavior of wiki pages and forms 2021-09-15 WU Vienna BSD-Style 2 - - + + 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 -r1.1.2.88 -r1.1.2.89 --- openacs-4/packages/xowf/lib/inclass-exam.wf 9 Jan 2022 20:46:52 -0000 1.1.2.88 +++ openacs-4/packages/xowf/lib/inclass-exam.wf 31 Jan 2022 13:15:42 -0000 1.1.2.89 @@ -60,7 +60,7 @@ set :policy ::xowf::test_item::test-item-policy-publish set :debug 0 set :live_updates 1 -set :QM ::xowf::test_item::question_manager + :forward QM ::xowf::test_item::question_manager set :fc_repository { @@ -540,8 +540,10 @@ # export results as CSV table # :proc www-exam-results {} { - set manual_gradings [:property manual_gradings] - set results [:property __results] + #set manual_gradings [:property manual_gradings] + set manual_gradings [xowf::test_item::answer_manager get_exam_results -obj [self] manual_gradings] + #set results [:property __results] + set results [xowf::test_item::answer_manager get_exam_results -obj [self] results] xowf::test_item::answer_manager results_export -reply -manual_gradings $manual_gradings $results ad_script_abort } @@ -561,7 +563,7 @@ -filter_id [:query_parameter id:int32 ""] \ -creation_user [:query_parameter creation_user:int32 ""] \ -revision_id [:query_parameter rid:int32 ""] \ - -form_objs [:query_parameter fos:int32 ""] \ + -filter_form_ids [:query_parameter fos:int32 ""] \ -export [:query_parameter export:boolean 0] \ -orderby [:query_parameter orderby:token "online-exam-userName"] \ -grading [:query_parameter grading:token [:property grading]] \ @@ -808,15 +810,17 @@ set formDict [ns_set array [ns_getform]] # - # Update property "manual_gradings" of the exam + # Update property "manual_gradings" of the exam in the results page # - set manual_gradings [:property manual_gradings] + #set manual_gradings [:property manual_gradings] + set manual_gradings [xowf::test_item::answer_manager get_exam_results -obj [self] manual_gradings] set user_id [dict get $formDict user_id] set qn [dict get $formDict question_name] dict set manual_gradings $user_id $qn achieved [dict get $formDict achieved] dict set manual_gradings $user_id $qn comment [dict get $formDict comment] - dict set :instance_attributes manual_gradings $manual_gradings - :update_attribute_from_slot [:find_slot instance_attributes] ${:instance_attributes} + xowf::test_item::answer_manager set_exam_results -obj [self] manual_gradings $manual_gradings + #dict set :instance_attributes manual_gradings $manual_gradings + #:update_attribute_from_slot [:find_slot instance_attributes] ${:instance_attributes} # # Recompute the achieved points percentage for the full exam Index: openacs-4/packages/xowf/resources/prototypes/inclass-exam-statistics.wf.page =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/resources/prototypes/Attic/inclass-exam-statistics.wf.page,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowf/resources/prototypes/inclass-exam-statistics.wf.page 31 Jan 2022 13:15:42 -0000 1.1.2.1 @@ -0,0 +1,28 @@ +# -*- 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-statistics.wf \ + -title "In-class Exam Statistics" \ + -instance_attributes { + workflow_definition { + :auto_form_constraints { + _title:label=CHANGED + _page_order:omit _creator:omit _text:omit _description:omit _nls_language:omit + statistics:textarea + results:textarea + manual_gradings:textarea + } + set :auto_form_template { + @statistics@ @results@ @manual_gradings@ + } + State initial + } + form_constraints {} + return_url {} + } + + + + 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.208 -r1.7.2.209 --- openacs-4/packages/xowf/tcl/test-item-procs.tcl 28 Jan 2022 07:50:54 -0000 1.7.2.208 +++ openacs-4/packages/xowf/tcl/test-item-procs.tcl 31 Jan 2022 13:15:42 -0000 1.7.2.209 @@ -1322,6 +1322,13 @@ namespace eval ::xowf::test_item { + # the fillowing is not yet ready for prime time. + if {0 && [acs::icanuse "nx::alias object"]} { + set register_command "alias" + } else { + set register_command "forward" + } + nx::Class create AssessmentInterface { # # Abstract class for common functionality @@ -1449,7 +1456,6 @@ # - form_name_based_attribute_stem # - name_to_question_obj_dict # - # - get_form_object # - rename_attributes # @@ -1581,20 +1587,20 @@ return $form_obj } - :public method -deprecated get_form_object {ctx:object form_name} { - # - # Return the form object based on the provided form name. This - # function performs attribute renaming on the returned form - # object. - # - set form_id [$ctx default_load_form_id $form_name] - set obj [$ctx object] - set form_obj [::xowiki::FormPage get_instance_from_db -item_id $form_id] - return [:rename_attributes $form_obj] - } + # :method -deprecated get_form_object {ctx:object form_name} { + # # + # # Return the form object based on the provided form name. This + # # function performs attribute renaming on the returned form + # # object. + # # + # set form_id [$ctx default_load_form_id $form_name] + # set obj [$ctx object] + # set form_obj [::xowiki::FormPage get_instance_from_db -item_id $form_id] + # return [:rename_attributes $form_obj] + # } } - AssessmentInterface forward FL \ + AssessmentInterface {*}$register_command FL \ [Renaming_form_loader create renaming_form_loader] } @@ -1968,7 +1974,8 @@ # # Delete as well the manual gradings for this exam. # - $obj set_property -new 1 manual_gradings {} + #$obj set_property -new 1 manual_gradings {} + :AM set_exam_results -obj $obj manual_gradings {} return $wf } @@ -2106,7 +2113,8 @@ dict set r examPublished [clock format $examPublishedClock -format "%H:%M:%S"] set epTimeDiff [expr {$toClock - $examPublishedClock}] dict set r examPublishedDuration "[expr {$epTimeDiff/60}]m [expr {$epTimeDiff%60}]s" - ns_log notice "EP examPublishedDuration [dict get $r examPublishedDuration] EP [dict get $r examPublished] $exam_published_time" + #ns_log notice "EP examPublishedDuration [dict get $r examPublishedDuration]" \ + "EP [dict get $r examPublished] $exam_published_time" dict set r examPublishedSeconds $epTimeDiff } return $r @@ -2326,6 +2334,7 @@ set totalPoints 0 set achievableTotalPoints 0 set details {} + foreach a [dict keys $answer_attributes] { set f [$submission lookup_form_field -name $a $all_form_fields] set points {} @@ -2341,6 +2350,7 @@ } else { set auto_correct_achieved "" } + #ns_log notice "=== achieved_points <$a> auto_correct_achieved $auto_correct_achieved" # # Manual grading has higher priority than autograding. @@ -2363,6 +2373,7 @@ auto_correct_achieved $auto_correct_achieved \ achievable $achievablePoints] } + #ns_log notice "final details <$details>" return [list achievedPoints $totalPoints \ details $details \ @@ -3010,13 +3021,13 @@ :method render_full_submission_form { -wf:object -submission:object - -form_objs + -filter_form_ids:integer,0..n } { # # Compute the HTML of the full submission with all form fields # instantiated according to randomization. # - # @param form_objs used for filtering questions + # @param filter_form_ids used for filtering questions # @return HTML of question form object containing all (wanted) questions # @@ -3042,7 +3053,7 @@ # xo::cc eval_as_user -user_id [$submission creation_user] { $submission set __feedback_mode 2 - $submission set __form_objs $form_objs + $submission set __form_objs $filter_form_ids set question_form [$submission render_content] } @@ -3303,7 +3314,7 @@ {-examWf:object} {-exam_question_dict} {-filter_id:integer,0..1 ""} - {-form_objs:integer,0..n ""} + {-filter_form_ids:integer,0..n ""} {-grading_scheme:object} {-recutil:object,0..1 ""} {-zipFile:object,0..1 ""} @@ -3318,7 +3329,7 @@ set userName [$submission set online-exam-userName] set fullName [$submission set online-exam-fullName] set user_id [$submission set creation_user] - set manual_gradings [$examWf property manual_gradings] + set manual_gradings [:get_exam_results -obj $examWf manual_gradings] set results "" #if {[$submission state] ne "done"} { @@ -3348,25 +3359,33 @@ # a subset, especially in cases, where filtering (e.g., show # only one question of all candidates) happens. # + set exam_question_objs [dict values $exam_question_dict] + set answeredAnswerAttributes \ [:FL answer_attributes [$submission instance_attributes]] set formAnswerAttributeNames \ - [dict keys [:FL name_to_question_obj_dict $form_objs]] + [dict keys [:FL name_to_question_obj_dict $exam_question_objs]] set usedAnswerAttributes {} foreach {k v} $answeredAnswerAttributes { if {$k in $formAnswerAttributeNames} { dict set usedAnswerAttributes $k $v } } + #ns_log notice "filter_form_ids <$filter_form_ids>" + #ns_log notice "question_objs <[dict get $combined_form_info question_objs]>" + #ns_log notice "answeredAnswerAttributes <$answeredAnswerAttributes>" + #ns_log notice "formAnswerAttributeNames <$formAnswerAttributeNames> [:FL name_to_question_obj_dict $filter_form_ids]" + #ns_log notice "usedAnswerAttributes <$usedAnswerAttributes>" + # # "render_full_submission_form" calls "summary_form" to obtain the # user's answers to all questions. # set question_form [:render_full_submission_form \ -wf $wf \ -submission $submission \ - -form_objs $form_objs] + -filter_form_ids $filter_form_ids] if {$recutil ne ""} { :export_answer \ @@ -3391,8 +3410,8 @@ -answer_attributes $usedAnswerAttributes] dict set achieved_points totalPoints $totalPoints - #ns_log notice "achieved_points [dict get $achieved_points details]" - #ns_log notice "manual_gradings [:dict_value $manual_gradings $user_id]" + #ns_log notice "user $user_id: achieved_points [dict get $achieved_points details]" + #ns_log notice "user $user_id: manual_gradings [:dict_value $manual_gradings $user_id]" foreach pd [:dict_value $achieved_points details] { set qn [dict get $pd attributeName] @@ -3542,7 +3561,7 @@ {-filter_id:integer,0..1 ""} {-creation_user:integer,0..1 ""} {-revision_id:integer,0..1 ""} - {-form_objs:integer,0..n ""} + {-filter_form_ids:integer,0..n ""} {-export:boolean false} {-orderby:token "online-exam-userName"} {-grading:token,0..n ""} @@ -3570,10 +3589,10 @@ return [list do_stream 0 HTML ""] } - if {$form_objs ne "" && $form_objs ni [dict get $combined_form_info question_objs]} { - ns_log warning "inclass-exam: ignore invalid form_obj '$form_objs';" \ + if {$filter_form_ids ne "" && $filter_form_ids ni [dict get $combined_form_info question_objs]} { + ns_log warning "inclass-exam: ignore invalid form_obj '$filter_form_ids';" \ "valid [dict get $combined_form_info question_objs]" - set form_objs "" + set filter_form_ids "" } ns_log notice "--- grading '$grading'" set grading_scheme [:grading_scheme -examWf $examWf -grading $grading -total_points $totalPoints] @@ -3597,12 +3616,12 @@ ::xo::cc set_parameter template_file view-plain-master ::xo::cc set_parameter MenuBar 0 - if {[llength $form_objs] > 0} { + if {[llength $filter_form_ids] > 0} { # # Filter by questions. For the time being, we allow only a # single question, ... and we take the first ones. # - append HTML "

#xowf.question#: [ns_quotehtml [[lindex $form_objs 0] title]]

\n" + append HTML "

#xowf.question#: [ns_quotehtml [[lindex $filter_form_ids 0] title]]

\n" set runtime_panel_view "" } elseif {$as_student} { @@ -3686,6 +3705,7 @@ set form_objs_exam [:QM load_question_objs $examWf [$examWf property question]] set question_dict [:FL name_to_question_obj_dict $form_objs_exam] + #ns_log notice "passed filter_form_ids <$filter_form_ids> form_objs_exam <$form_objs_exam>" # # Iterate over the items sorted by orderby. @@ -3701,7 +3721,7 @@ -autograde $autograde \ -combined_form_info $combined_form_info \ -filter_id $filter_id \ - -form_objs $form_objs \ + -filter_form_ids $filter_form_ids \ -grading_scheme $grading_scheme \ -recutil $recutil \ -zipFile $zipFile \ @@ -3742,6 +3762,7 @@ # The following lines are conveniant for debugging # #set manual_gradings [$examWf property manual_gradings] + #set manual_gradings [:get_exam_results -obj $examWf manual_gradings] #append HTML
$manual_gradings
#append HTML
[:results_export -manual_gradings $manual_gradings $results]
} @@ -3760,8 +3781,8 @@ # # Avoid empty entries for query parameters # - if {[llength $form_objs] > 0} { - set fos $form_objs + if {[llength $filter_form_ids] > 0} { + set fos $filter_form_ids } foreach value {revision_id filter_id} var {rid id} { if {[set $value] ne ""} { @@ -3798,10 +3819,9 @@ $examWf unset $var } } - dict set ia __statistics $statistics + :AM set_exam_results -obj $examWf statistics $statistics } - dict set ia __results $results - $examWf update_attribute_from_slot [$examWf find_slot instance_attributes] $ia + :AM set_exam_results -obj $examWf results $results } return [list do_stream $do_stream HTML $HTML] @@ -4656,9 +4676,62 @@ }] } } + #---------------------------------------------------------------------- + # Class: Answer_manager + # Method: get_exam_results + #---------------------------------------------------------------------- + :public method get_exam_results { + -obj:object,required + property + {default ""} + } { + set p [$obj childpage -name en:result -form inclass-exam-statistics.wf] + set instance_attributes [$p instance_attributes] + if {[dict exists $instance_attributes $property]} { + #ns_log notice "get_exam_results <$property> returns value from " \ + "results page: [dict get $instance_attributes $property]" + return [dict get $instance_attributes $property] + } + #ns_log notice "get_exam_results <$property> returns default" + return $default + } + + #---------------------------------------------------------------------- + # Class: Answer_manager + # Method: set_exam_results + #---------------------------------------------------------------------- + :public method set_exam_results { + -obj:object,required + property + value + } { + #ns_log notice "SES '$property' bytes [string length $value]" + set p [$obj childpage -name en:result -form inclass-exam-statistics.wf] + set instance_attributes [$p instance_attributes] + dict set instance_attributes $property $value + $p update_attribute_from_slot [$p find_slot instance_attributes] ${instance_attributes} + # + # cleanup of legacy values + # + set instance_attributes [$obj instance_attributes] + foreach property_name [list $property __$property] { + if {[dict exists $instance_attributes $property_name]} { + ns_log notice "SES set_exam_results:" \ + "clearing values from earlier releases for '$property_name'" \ + "was <[dict get $instance_attributes $property_name]>" + dict unset instance_attributes $property_name + $obj set instance_attributes $instance_attributes + #ns_log notice "FINAL IA <$instance_attributes> for item_id [$obj item_id]" \ + "revision_id [$obj revision_id]" + $obj update_attribute_from_slot [$obj find_slot instance_attributes] $instance_attributes + ::xo::xotcl_object_cache flush [$obj item_id] + ::xo::xotcl_object_cache flush [$obj revision_id] + } + } + } } - AssessmentInterface forward AM \ + AssessmentInterface {*}$register_command AM \ [Answer_manager create answer_manager] } @@ -6349,7 +6422,7 @@ # # Provide a summary of all questions of an exam. # - set results [$obj property __results] + set results [:AM get_exam_results -obj $obj results] if {$results ne ""} { set href [$obj pretty_link -query m=exam-results] set results_summary [subst { @@ -6460,7 +6533,7 @@ # instance. These statistics are computed when the exam # protocol is rendered. # - set statistics [$obj property __statistics] + set statistics [:AM get_exam_results -obj $obj statistics] if {$statistics ne ""} { foreach var {success_statistics count_statistics} key {success count} { if {[dict exists $statistics $key]} { @@ -6792,7 +6865,7 @@ } } set qm [Question_manager create question_manager] - AssessmentInterface forward QM $qm + AssessmentInterface {*}$register_command QM $qm ::xowiki::formfield::TestItemField instforward QM $qm } 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.68 -r1.28.2.69 --- openacs-4/packages/xowf/tcl/xowf-procs.tcl 26 Jan 2022 07:52:43 -0000 1.28.2.68 +++ openacs-4/packages/xowf/tcl/xowf-procs.tcl 31 Jan 2022 13:15:42 -0000 1.28.2.69 @@ -64,11 +64,12 @@ online-exam.wf inclass-quiz.wf inclass-exam.wf + inclass-exam-statistics.wf edit-interaction.wf edit-grading-scheme.wf answer-single-question.wf topic-assignment.wf - + quiz-select_question.form select_question.form select-topics.form @@ -777,7 +778,7 @@ Context instproc initialize_context {obj} { #:log "START-initialize_context <$obj>" # - # Keep the object in instance variable + # Keep the object in an instance variable. # set :object $obj @@ -790,12 +791,18 @@ :set_current_state $state if {![nsf::is object ${:current_state}]} { - # The state was probably deleted from the workflow definition, - # but the workflow instance does still need it. We complain an - # reset the state to initial, which should be always present. - :log "===== Workflow instance [$obj name] is in an undefined state '$state', reset to initial" - $obj msg "Workflow instance [$obj name] is in an undefined state '$state', reset to initial" - :set_current_state initial + if {$state eq "initial"} { + ns_log warning "no state object ${:current_state}" + } else { + # + # The state was probably deleted from the workflow definition, + # but the workflow instance does still need it. We complain an + # reset the state to "initial", which should be always present. + # + :log "===== Workflow instance [$obj name] is in an undefined state '$state', reset to initial" + $obj msg "Workflow instance [$obj name] is in an undefined state '$state', reset to initial" + :set_current_state initial + } } # Set the embedded_context to the workflow context, @@ -2390,6 +2397,46 @@ return [$actionObj invoke -attributes $attributes] } + + WorkflowPage ad_instproc childpage {-name:required -form} { + + Return the child page of the current object with the provided + name. In case the child object does not exist, create it as an + instance of the provided form. + + @return page object + } { + if {[info exists form]} { + set child_page_id [::${:package_id} lookup \ + -use_package_path false \ + -default_lang en \ + -name $name \ + -parent_id ${:item_id}] + if {$child_page_id == 0} { + ns_log notice "child page '$name' does not exist" + set form_obj [::${:package_id} instantiate_forms \ + -default_lang "en" \ + -forms $form] + if {[llength $form_obj] == 0} { + error "childpage: cannot instantiate $form" + } + set p [$form_obj create_form_page_instance \ + -name $name \ + -nls_language en_US \ + -parent_id ${:item_id} \ + -package_id ${:package_id} \ + -instance_attributes {}] + $p save_new + } else { + #ns_log notice "child page '$name' exists already (item_id $child_page_id)" + set p [::xo::db::CrClass get_instance_from_db -item_id $child_page_id] + } + return $p + } else { + error "cannot create '$name': API supports so far only form pages" + } + } + # # Interface to atjobs # Index: openacs-4/packages/xowf/tcl/test/test-item-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowf/tcl/test/Attic/test-item-procs.tcl,v diff -u -N -r1.1.2.27 -r1.1.2.28 --- openacs-4/packages/xowf/tcl/test/test-item-procs.tcl 31 Jan 2022 13:05:45 -0000 1.1.2.27 +++ openacs-4/packages/xowf/tcl/test/test-item-procs.tcl 31 Jan 2022 13:15:42 -0000 1.1.2.28 @@ -23,6 +23,7 @@ "::xo::ConnectionContext instproc eval_as_user" "::xowf::Package instproc destroy" + "::xowf::WorkflowPage instproc childpage" "::xowf::WorkflowPage instproc get_revision_sets" "::xowf::WorkflowPage instproc is_wf" "::xowf::WorkflowPage instproc is_wf_instance" @@ -40,6 +41,7 @@ "::xowf::test_item::Answer_manager instproc get_answer_wf" "::xowf::test_item::Answer_manager instproc get_answers" "::xowf::test_item::Answer_manager instproc get_duration" + "::xowf::test_item::Answer_manager instproc get_exam_results" "::xowf::test_item::Answer_manager instproc get_wf_instances" "::xowf::test_item::Answer_manager instproc grading_dialog_setup" "::xowf::test_item::Answer_manager instproc grading_table" @@ -51,6 +53,7 @@ "::xowf::test_item::Answer_manager instproc render_answers" "::xowf::test_item::Answer_manager instproc revisions_up_to" "::xowf::test_item::Answer_manager instproc runtime_panel" + "::xowf::test_item::Answer_manager instproc set_exam_results" "::xowf::test_item::Answer_manager instproc state_periods" "::xowf::test_item::Answer_manager instproc student_submissions_exist" "::xowf::test_item::Question_manager instproc add_seeds"