%3 ::xowiki::formfield::FormField ::xowiki::formfield::FormField → fc_decode → fc_encode → get_from_name → get_single_spec → interprete_condition CSSclass_list_add add_statistics answer_check=AND answer_check=answer_words answer_check=btwn answer_check=contains answer_check=contains-not answer_check=eq answer_check=ge answer_check=gt answer_check=in answer_check=le answer_check=lt answer_check=match answer_is_correct asWidgetSpec behavior booleanAttributes config_from_spec convert_to_external convert_to_internal describe dict_to_fc dict_to_spec dict_value escape_message_keys field_value handle_transmit_always has_instance_variable init initialize interprete_single_spec is_disabled is_repeat_template_p leaf_components localize make_correct pretty_image pretty_value process_correct_when_modifier remove_omit render render_answer_statistics render_collapsed render_disabled_as_div render_error_msg render_form_widget render_help_text render_input render_item render_localizer render_modal render_result_statistics render_word_statistics repeat repeat_add_label resetBooleanAttributes reset_on_validation_error reset_parameter same_value set_disabled set_feedback set_is_repeat_template stats_record_count validate validation_check value_if_nothing_is_returned_from_form word_statistics ::xo::tdom::Object ::xo::tdom::Object ::xowiki::formfield::FormField->::xo::tdom::Object ::xowiki::formfield::ShuffleField ::xowiki::formfield::ShuffleField check=options initialize randomized_indices shuffle_options valid_subselection ::xowiki::formfield::ShuffleField->::xowiki::formfield::FormField ::xowiki::formfield::text_fields ::xowiki::formfield::text_fields answer_is_correct get_text_entry initialize pretty_value render_help_text render_input set_feedback td_pretty_value ::xowiki::formfield::text_fields->::xowiki::formfield::ShuffleField ::xowiki::formfield::CompoundField ::xowiki::formfield::CompoundField ::xowiki::formfield::text_fields->::xowiki::formfield::CompoundField ::xowiki::formfield::enumeration ::xowiki::formfield::enumeration add_statistics answer_is_correct config_from_category_tree get_labels ggw initialize make_correct pretty_value render_input render_label_classes render_label_text render_result_statistics scores stats_record_detail value_if_nothing_is_returned_from_form ::xowiki::formfield::enumeration->::xowiki::formfield::ShuffleField ::xowiki::formfield::checkbox ::xowiki::formfield::checkbox initialize render_input td_pretty_value ::xowiki::formfield::checkbox->::xowiki::formfield::enumeration ::xowiki::formfield::select ::xowiki::formfield::select initialize render_input ::xowiki::formfield::select->::xowiki::formfield::enumeration ::xowiki::formfield::radio ::xowiki::formfield::radio initialize render_input ::xowiki::formfield::radio->::xowiki::formfield::enumeration

Class ::xowiki::formfield::ShuffleField

::xowiki::formfield::ShuffleField[i] create ... \
           [ -options (default "") ] \
           [ -render_hints (default "") ] \
           [ -show_max (default "") ] \
           [ -shuffle_kind:wordchar (default "none") ]

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.
Documented Parameters:
shuffle_kind
none|peruser|always
Defined in packages/xowiki/tcl/form-field-procs.tcl

Class Relations

  • class: ::xotcl::Class[i]
  • superclass: ::xowiki::formfield::FormField[i]
  • subclass: ::xowiki::formfield::text_fields[i], ::xowiki::formfield::enumeration[i]
::xotcl::Class create ::xowiki::formfield::ShuffleField \
     -superclass ::xowiki::formfield::FormField

Methods (to be applied on instances)

  • check=options (scripted)

    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
  • initialize (scripted)

    next
    
    #
    # Shuffle options when needed
    #
    if {${:shuffle_kind} ne "none"} {
      :shuffle_options
    }
  • options (setter)

  • randomized_indices (scripted)

    #
    # 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
  • render_hints (setter)

  • show_max (setter)

  • shuffle_kind (setter)

  • shuffle_options (scripted)

    #
    # 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
      }
    }
  • valid_subselection (scripted)

    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
  • validator (setter)