Class ::xowiki::formfield::ShuffleField (public)
::xotcl::Class ::xowiki::formfield::ShuffleField \ [ -shuffle_kind shuffle_kind ]
Defined in /var/www/openacs.org/packages/xowiki/tcl/form-field-procs.tcl
An abstract class for shuffling options and answers. The options can be used a content of checkboxes, radioboxes and the like. This is particular useful when creating quizzes.
- Switches:
- -shuffle_kind (optional)
- none|peruser|always
- Testcases:
- No testcase defined.
Source code: namespace eval ::xowiki::formfield {} ::nsf::object::alloc ::xotcl::Class ::xowiki::formfield::ShuffleField {set :__default_metaclass ::xotcl::Class set :__default_superclass ::xotcl::Object set :abstract 1} ::xowiki::formfield::ShuffleField instproc shuffle_options {} { # # Reorder :options and :answers when :shuffle is activated. # set length [llength ${:options}] if {$length > 0} { # # There is something to shuffle. # # # Produce a list of random indices. # set shuffled [:randomized_indices $length] # # Use the random indices for reordering the :options and # :answers. # if {[info exists :show_max] && ${:show_max} ne ""} { set shuffled [:valid_subselection $shuffled] #ns_log notice "SHUFFLE ${:name} <$shuffled> answer_value ${:answer_value} MAX <${:show_max}>" } set option2 {}; set answer2 {}; set answer_value2 {} if {[llength ${:render_hints}] > 0} { set render_hints2 {} } if {[info exists :descriptions] && [llength ${:descriptions}] > 0} { set descriptions2 {} } foreach i $shuffled { lappend option2 [lindex ${:options} $i] lappend answer2 [lindex ${:answer} $i] if {${:multiple} && [info exists :answer_value]} { lappend answer_value2 [lindex ${:answer_value} $i] } if {[info exists render_hints2]} { lappend render_hints2 [lindex ${:render_hints} $i] } if {[info exists descriptions2]} { lappend descriptions2 [lindex ${:descriptions} $i] } } #ns_log notice "SHUFFLE ${:name} o2=$option2 answer2=$answer2" set :options $option2 set :answer $answer2 if {${:multiple}} { set :answer_value $answer_value2 } if {[info exists render_hints2]} { set :render_hints $render_hints2 } if {[info exists descriptions2]} { set :descriptions $descriptions2 } } } ::xowiki::formfield::ShuffleField instproc initialize {} { next # # Shuffle options when needed # if {${:shuffle_kind} ne "none"} { :shuffle_options } } ::xowiki::formfield::ShuffleField instproc check=options value { set result 1 if {$value ne "" && [info exists :options]} { set allowed_values [lmap option ${:options} {lindex $option 1}] if {!${:multiple}} { set value [list $value] } if {[string is list $value]} { foreach v $value { if {$v ni $allowed_values} { set result 0 break } } } else { set result 0 } if {$result == 0} { # # Report for the time being invalid validate from option # fields. # ns_log notice "OPTIONS CHECK ${:name} <$value> in <$allowed_values> -> $result" "([:info class])" } } return $result } ::xowiki::formfield::ShuffleField instproc randomized_indices length { # # Produce a list of random indices. # # In case, the shuffle_kind is not "always", we assume a shuffling # produced by every call. When a seed is provided (e.g. a user_id) # then the shuffling is stable for this seed. # if {${:shuffle_kind} ne "always"} { # # It is possible to keep different seeds in the instance # attributes of the object to support a different randomization # not only by user but also per position. This requires either # an instance variable "test_item_in_position" in the form field or an # instance_attribute "position" in the object, where the former # has a higher precedence (important for combined forms). # if {![info exists :test_item_in_position]} { set :test_item_in_position [${:object} property position] #ns_log notice "${:name} randomized_indices get position ${:test_item_in_position} from property" } else { #ns_log notice "${:name} randomized_indices position ${:test_item_in_position} already set (user [::xo::cc user_id])" } set seeds [${:object} property seeds] set seed [expr {$seeds ne "" && ${:test_item_in_position} ne "" ? [lindex $seeds ${:test_item_in_position}] : [xo::cc user_id]}] set shuffled [::xowiki::randomized_indices -seed $seed $length] #ns_log notice "${:name} randomized_indices for seed $seed (user_id [xo::cc user_id])" "(${:test_item_in_position} - $seeds): $shuffled" } else { set shuffled [::xowiki::randomized_indices $length] } return $shuffled } ::xowiki::formfield::ShuffleField instproc valid_subselection shuffled { if {${:show_max} < [llength $shuffled]} { # # Take first n shuffled elements as subselection # set range [expr {${:show_max} - 1}] set subselection [lrange $shuffled 0 $range] if {${:multiple}} { # # Multiple choice: Accept every subselection as valid for the # time being. # if {[info exists :grading] && ${:grading} in {wi1 wi2 canvas etk} && [info exists :answer_value] } { # # These grading schemes require that at least one alternative is true. # set nr_correct 0 foreach v ${:answer_value} { if {$v - 1 in $subselection} { incr nr_correct 1 } } #ns_log notice "XXXX subselection <$subselection> answer_value ${:answer_value} shuffled <$shuffled> -> $nr_correct" if {$nr_correct == 0} { # # Take a random correct value and insert this at a random position. # set answerIndex [expr {int([llength ${:answer_value}] * rand())}] set dropIndex [expr {int(${:show_max} * rand())}] set subselection [lreplace $subselection $dropIndex $dropIndex [expr {[lindex ${:answer_value} $answerIndex] - 1}]] ns_log notice "XXXX selected correctIndex $answerIndex dropIndex $dropIndex -> $subselection" } } } elseif {[info exists :answer_value]} { # # Single choice: we have exactly one value which is correct, # which is the "answer_value". Make sure that this correct # element is included in subselection. # set must_contain [expr {${:answer_value} - 1}] if {$must_contain ni $subselection} { # # It is not included. Drop a random element from the range # of answers and insert there the correct value. # #ns_log notice "--- have to fix subselection does not contain $must_contain" set dropIndex [expr {int($range * rand())}] set subselection [lreplace $subselection $dropIndex $dropIndex $must_contain] #ns_log notice "--- fixed subselection dropIndex $dropIndex -> $subselection" } } set shuffled $subselection } return $shuffled } ::xowiki::formfield::ShuffleField instparametercmd options ::xowiki::formfield::ShuffleField instparametercmd render_hints ::xowiki::formfield::ShuffleField instparametercmd validator ::xowiki::formfield::ShuffleField instparametercmd show_max ::xowiki::formfield::ShuffleField instparametercmd shuffle_kind ::nsf::relation::set ::xowiki::formfield::ShuffleField superclass ::xowiki::formfield::FormField ::nx::slotObj -container slot ::xowiki::formfield::ShuffleField ::xowiki::formfield::ShuffleField::slot eval {set :__parameter { {options ""} {render_hints ""} {show_max ""} {shuffle_kind:wordchar none} }} ::nsf::object::alloc ::xotcl::Attribute ::xowiki::formfield::ShuffleField::slot::shuffle_kind {set :accessor public set :configurable true set :convert false set :default none set :defaultmethods {} set :disposition alias set :domain ::xowiki::formfield::ShuffleField set :incremental 0 set :manager ::xowiki::formfield::ShuffleField::slot::shuffle_kind set :methodname shuffle_kind set :multiplicity 1..1 set :name shuffle_kind set :per-object false set :position 0 set :required false set :substdefault 0b111 set :trace none set :type wordchar : init} ::nsf::object::alloc ::xotcl::Attribute ::xowiki::formfield::ShuffleField::slot::show_max {set :accessor public set :configurable true set :convert false set :default {} set :defaultmethods {} set :disposition alias set :domain ::xowiki::formfield::ShuffleField set :incremental 0 set :manager ::xowiki::formfield::ShuffleField::slot::show_max set :methodname show_max set :multiplicity 1..1 set :name show_max set :per-object false set :position 0 set :required false set :substdefault 0b111 set :trace none : init} ::nsf::object::alloc ::xotcl::Attribute ::xowiki::formfield::ShuffleField::slot::validator {set :accessor public set :configurable true set :convert false set :default options set :defaultmethods {} set :disposition alias set :domain ::xowiki::formfield::ShuffleField set :incremental false set :manager ::xowiki::formfield::ShuffleField::slot::validator set :methodname validator set :multiplicity 1..1 set :name validator set :per-object false set :position 0 set :required false set :substdefault 0b111 set :trace none : init} ::nsf::object::alloc ::xotcl::Attribute ::xowiki::formfield::ShuffleField::slot::options {set :accessor public set :configurable true set :convert false set :default {} set :defaultmethods {} set :disposition alias set :domain ::xowiki::formfield::ShuffleField set :incremental 0 set :manager ::xowiki::formfield::ShuffleField::slot::options set :methodname options set :multiplicity 1..1 set :name options set :per-object false set :position 0 set :required false set :substdefault 0b111 set :trace none : init} ::nsf::object::alloc ::xotcl::Attribute ::xowiki::formfield::ShuffleField::slot::render_hints {set :accessor public set :configurable true set :convert false set :default {} set :defaultmethods {} set :disposition alias set :domain ::xowiki::formfield::ShuffleField set :incremental 0 set :manager ::xowiki::formfield::ShuffleField::slot::render_hints set :methodname render_hints set :multiplicity 1..1 set :name render_hints set :per-object false set :position 0 set :required false set :substdefault 0b111 set :trace none : init}XQL Not present: Generic, PostgreSQL, Oracle