Class ::xowiki::formfield::ShuffleField (public)

 ::xotcl::Class ::xowiki::formfield::ShuffleField[i] \
    [ -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
[ hide source ] | [ make this the default ]
Show another procedure: