%3 ::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::ShuffleField ::xowiki::formfield::ShuffleField check=options initialize randomized_indices shuffle_options valid_subselection ::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::FormField ::xowiki::formfield::FormField ::xowiki::formfield::ShuffleField->::xowiki::formfield::FormField ::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 ::xowiki::formfield::boolean_checkbox ::xowiki::formfield::boolean_checkbox check=options initialize value_if_nothing_is_returned_from_form ::xowiki::formfield::boolean_checkbox->::xowiki::formfield::checkbox ::xowiki::formfield::month ::xowiki::formfield::month initialize ::xowiki::formfield::month->::xowiki::formfield::select ::xowiki::formfield::HH24 ::xowiki::formfield::HH24 initialize ::xowiki::formfield::HH24->::xowiki::formfield::select ::xowiki::formfield::iprange ::xowiki::formfield::iprange initialize ::xowiki::formfield::iprange->::xowiki::formfield::select ::xowiki::formfield::candidate_box_select ::xowiki::formfield::candidate_box_select add_bulk_handler add_drag_handler render_input ::xowiki::formfield::candidate_box_select->::xowiki::formfield::select ::xowiki::formfield::security_policy ::xowiki::formfield::security_policy initialize ::xowiki::formfield::security_policy->::xowiki::formfield::select ::xowiki::formfield::MM ::xowiki::formfield::MM initialize ::xowiki::formfield::MM->::xowiki::formfield::select ::xowiki::formfield::MI ::xowiki::formfield::MI initialize value ::xowiki::formfield::MI->::xowiki::formfield::select ::xowiki::formfield::DD ::xowiki::formfield::DD initialize ::xowiki::formfield::DD->::xowiki::formfield::select ::xowiki::formfield::bootstrap-select ::xowiki::formfield::bootstrap-select initialize render_input ::xowiki::formfield::bootstrap-select->::xowiki::formfield::select ::xowiki::formfield::grading_scheme ::xowiki::formfield::grading_scheme initialize ::xowiki::formfield::grading_scheme->::xowiki::formfield::select ::xowiki::formfield::reorder_box ::xowiki::formfield::reorder_box answer_is_correct initialize render_input ::xowiki::formfield::reorder_box->::xowiki::formfield::select ::xowiki::formfield::class ::xowiki::formfield::class initialize ::xowiki::formfield::class->::xowiki::formfield::select ::xowiki::formfield::mon ::xowiki::formfield::mon initialize ::xowiki::formfield::mon->::xowiki::formfield::select ::xowiki::formfield::boolean ::xowiki::formfield::boolean initialize value_if_nothing_is_returned_from_form ::xowiki::formfield::boolean->::xowiki::formfield::radio ::xowiki::formfield::scale ::xowiki::formfield::scale initialize ::xowiki::formfield::scale->::xowiki::formfield::radio

Class ::xowiki::formfield::enumeration

::xowiki::formfield::enumeration[i] create ... \
           [ -category_tree category_tree ] \
           [ -descriptions (default "") ]

Defined in

Class Relations

  • class: ::xotcl::Class[i]
  • superclass: ::xowiki::formfield::ShuffleField[i]
  • subclass: ::xowiki::formfield::checkbox[i], ::xowiki::formfield::select[i], ::xowiki::formfield::radio[i]
::xotcl::Class create ::xowiki::formfield::enumeration \
     -superclass ::xowiki::formfield::ShuffleField

Methods (to be applied on instances)

  • add_statistics (scripted)

    #ns_log notice "???? add_statistics"
    #
    # Add generic statistics
    #
    next
    #
    # Enumeration specific statistics
    #
    #ns_log notice "[self] enumeration add_statistics (options $options) value <${:value}>"
    foreach v ${:value} {
      dict incr :result_statistics $v
    }
    #ns_log notice "${:name} ### answer ${:answer} value ${:value} correction ${:correction} "
    #ns_log notice [:serialize]
  • answer_is_correct (scripted)

    #:log "enumeration CORRECT? ${:name} (value=[:value], answer=[expr {[info exists :answer]?${:answer}:{NONE}}]"
    if {![info exists :answer]} {
      return 0
    } else {
      #
      # The question was answered, therefore, we can count it in the
      # statistics.
      #
      :stats_record_count
    
      set value [:value]
      #:log "enumeration ${:name} CORRECT? answers [llength ${:answer}] options [llength ${:options}]"
      set :correction {}
      set r 0; set f 0; set rk 0; set fk 0; set W 0; set O 0; set R 0
      foreach o ${:options} a ${:answer} {
        lassign $o label v
        #:log "enumeration ${:name} CORRECT? <$a> <$v in $value> -> [expr {$v in $value}]"
        #
        # A correct answer might be:
        # - a mark on a correct entry
        # - no mark on a wrong entry
        #
        if {$a} {
          incr r
          set correctly_answered [expr {$v in $value}]
        } else {
          set correctly_answered [expr {$v ni $value}]
          incr f
          #:log "enumeration ${:name} CORRECT? <$a> <$v ni $value> -> [expr {$v ni $value}]"
        }
    
        #:log "[self] ${:name} enumeration ${:name} CORRECT $o -> $correctly_answered"
        :stats_record_detail -label $label -value $v $correctly_answered
    
        lappend :correction $correctly_answered
        if {$correctly_answered} {
          incr R
        } else {
          incr W
        }
    
        if {$v in $value} {
          #
          # Marked entries: mark can be correct or wrong.
          #
          if {$a} {
            incr rk
          } else {
            incr fk
          }
        }
        set scores [:scores -r $r -f $f -rk $rk -fk $fk -R $R -W $W]
        set :correction_data [list  item [list r $r f $f]  marks [list rk $rk fk $fk]  answers [list R $R W $W]  scores $scores]
      }
      #:log "enumeration CHECKED CORRECT? ${:correction_data}"
      return [expr {0 ni ${:correction} ? 1 : -1}]
    }
  • category_tree (setter)

  • config_from_category_tree (scripted)

    # Get the options of a select or radio from the specified
    # category tree.
    #
    # We could config as well from the mapped category tree,
    # and get required and multiple from there....
    #
    # The usage of the label does not seem to be very useful.
    #
    #set tree_id [category_tree::get_id $tree_name [:locale]]
    
    set package_id [${:object} package_id]
    set tree_ids [::xowiki::Category get_mapped_trees  -object_id $package_id -locale ${:locale}  -names $tree_name -output tree_id]
    
    # In case there are multiple trees with the same name,
    # take the first one.
    #
    set tree_id [lindex $tree_ids 0]
    
    if {$tree_id eq ""} {
      :msg "cannot lookup mapped category tree name '$tree_name'"
      return
    }
    set subtree_id ""
    set options [list]
    
    foreach category [::xowiki::Category get_category_infos  -subtree_id $subtree_id -tree_id $tree_id] {
      lassign $category category_id category_name deprecated_p level
      set category_name [ns_quotehtml [lang::util::localize $category_name]]
      set :category_label($category_id$category_name
      if { $level>1 } {
        set category_name "[string repeat {.} [expr {2*$level-4}]]..$category_name"
      }
      lappend options [list $category_name $category_id]
    }
    set :options $options
    if {[info exists :default] && ${:default} ne ""} {
      #
      # When a default is provided, and the default is a valid
      # name. Note that the "symbolic" default has to be provided
      # exactly like the label, and it might not be unique.
      #
      set optdict [concat {*}$options]
      if {[dict exists $optdict ${:default}]} {
        set :default [dict get $optdict ${:default}]
      }
    }
    set :is_category_field 1
    # :msg label_could_be=$tree_name,existing=${:label}
    # if {![info exists :label]} {
    #    :label $tree_name
    # }
  • descriptions (setter)

  • get_labels (scripted)

    if {${:multiple}} {
      set labels [list]
      foreach v $values {
        lappend labels [list [:get_entry_label $v$v]
      }
      return $labels
    } else {
      return [list [list [:get_entry_label $values$values]]
    }
  • ggw (scripted)

    return [expr {100.0 * ($R - $W*0.5) / ($R + $W) }]
  • initialize (scripted)

    if {[info exists :category_tree]} {
      :config_from_category_tree ${:category_tree}
    }
    if {[info exists :answer]} {
      set count 1
      set :answer_value {}
      try {
        foreach a ${:answer} {
          if {$a} {
            lappend :answer_value $count
          }
          incr count
        }
      } on error {errorMsg} {
        ns_log error "${:name}: invalid answer value provided '${:answer}': must be list of booleans"
        error $errorMsg
      }
      #ns_log notice "???? answer ${:answer} -> ${:answer_value}"
    }
    next
    
    #
    # For required enumerations, the implicit default value is the
    # first entry of the options. This is as well the value, which is
    # returned from the browser in such cases.
    #
    if {${:required} && ${:value} eq ""} {
      set :value [lindex ${:options} 0 1]
    }
  • make_correct (scripted)

    if {[info exists :answer_value]} {
      set :value ${:answer_value}
      #ns_log notice "???? make_correct sets value ${:answer_value}"
    }
  • pretty_value (scripted)

    if {[info exists :category_label($v)]} {
      return [set :category_label($v)]
    }
    if {[info exists :multiple] && ${:multiple}} {
      foreach o ${:options} {
        lassign $o label value
        set labels($value) [:localize $label]
      }
      set values [list]
      foreach i $v {lappend values $labels($i)}
      return [join $values {, }]
    } else {
      foreach o ${:options} {
        lassign $o label value
        if {$value eq $v} {return [:localize $label]}
      }
    }
  • render_input (scripted)

    if {![::xotcl::self isnextcall]} {
      error "Abstract method render_input  called"
    } else {::xotcl::next}
  • render_label_classes (scripted)

    #
    # Determine the values of the CSS classes for correct/incorrect
    # rendering. In statistics mode (when :result_statistics exists),
    # use the correct value of the alternative. Otherwise, use
    # the :correction of the actual value in the form field.
    #
    if {[info exists :result_statistics]} {
      set values ${:answer}
      #ns_log notice "==== radio answer $answer aw ${:answer_value} results ${:result_statistics}"
    } else {
      set values [expr {[info exists :correction] ? ${:correction} : ""}]
    }
    return [lmap v $values {dict get {"" "" 1 correct 0 incorrect t correct f incorrect} $v}]
  • render_label_text (scripted)

    #
    # Render a label text (typically of a checkbox or radio input)
    # either as richtext or as plain label.
    #
    if {${:richtext}} {
      ::html::div -class richtext-label {
        ::html::t -disableOutputEscaping $label
        if {[info exists :evaluated_answer_result]
            && "incorrect" in $CSSclass
            && $description ne ""
          } {
          html::div -class "help-block description" {
            html::t $description
          }
        }
      }
    } else {
      ::html::t $label "
    }
  • render_result_statistics (scripted)

    #
    # In case, there are result_statistics, use a "progress bar" to
    # visualize correct answers per alternative ($rep).
    #
    if {[info exists :result_statistics] && [dict exists ${:result_statistics} count]} {
      #
      # result_count:    how often was question answered (in general)
      # correct_count:   how often was an alternative correctly answered
      # incorrect_count: how often was an alternative incorrectly answered
      #
      #set result_count [dict get ${:result_statistics} count]
      set alternative_counts [expr {[dict exists ${:result_statistics} $rep]
                                   ? [dict get ${:result_statistics} $rep]
                                   : ""}]
      set incorrect_count 0; set correct_count 0
      if {$alternative_counts ne ""} {
        foreach key {0 1} var {incorrect_count correct_count} {
          if {[dict exists $alternative_counts $key]} {
            set $var [dict get $alternative_counts $key]
          }
        }
      }
      set answered_count [expr {$correct_count + $incorrect_count}]
      if {$answered_count > 0} {
        set with_pie_charts [::xo::cc query_parameter pie:boolean 0]
    
        if {$with_pie_charts} {
          if {![info exists ::__xotcl_highcharts_pie]} {
            if {[template::head::can_resolve_urn urn:ad:js:highcharts]} {
              #
              # The highcharts package is available
              #
              template::add_body_script -src urn:ad:js:highcharts
            } else {
              #
              # The highcharts package is not available, go straight to the CDN.
              #
              template::add_body_script -src "//code.highcharts.com/highcharts.js"
              security::csp::require script-src code.highcharts.com
            }
          }
          set graphID pie-[incr ::__xotcl_highcharts_pie]
        }
        ::html::div -class container {
          ::html::div -class row {
            ::html::span -class "col-sm-2" -style "font-size: x-small; float: right;" {
              ::html::t "$correct_count of $answered_count correct"
            }
            if {$with_pie_charts} {
              ::html::div -class "col-sm-2"  -style "width: 400px; height:120px;" -id $graphID {}
            } else {
              ::html::div -class "progress col-sm-8"  -style "padding: 0px 0px 0px 0px;" {
                    set percentage [format %2.0f [expr {$correct_count * 100.0 / $answered_count}]]
                    ::html::div -class "progress-bar progress-bar-success" -role "progressbar"  -aria-valuenow $percentage -aria-valuemin "0" -aria-valuemax "100" -style "width:$percentage%" {
                          if {$percentage > 0} {
                            ::html::t "$percentage % correct"
                          }
                        }
                  }
            }
          }
        }
        if {$with_pie_charts} {
          set startAngle [expr {$correct_count == 0 || $incorrect_count == 0 ? 90: 0}]
          template::add_body_script -script [subst [ns_trim {
            Highcharts.chart('$graphID', {
              chart: {type: 'pie'},
              plotOptions: {pie: {size: 35, startAngle: $startAngle }},
              title: {text: ''},
              colors: \['green', 'red'\],
              credits: {enabled: false },
              series: \[{name: 'Results', data: \[ {name:'correct', y: $correct_count}, {name:'incorrect', y: $incorrect_count}\]}\]
            });
          }]]
        }
      }
    }
  • scores (scripted, public)

     <instance of xowiki::formfield::enumeration[i]> scores [ -r r ] \
        [ -f f ] [ -rk rk ] [ -fk fk ] [ -R R ] [ -W W ]
    Switches:
    -r (optional, defaults to "0")
    number of answers which are true
    -f (optional, defaults to "0")
    number of answers which are false
    -rk (optional, defaults to "0")
    number checkmarks to a true answer
    -fk (optional, defaults to "0")
    number checkmarks to a false answer
    -R (optional)
    number correct answered
    -W (optional)
    number incorrect answered

    Testcases:
    create_test_items
    #
    # Now calculate the scores of different scoring schemes.
    #
    if {$r > 0} {
      #
      # Certain correction schemes divide by $r. We cannot use
      # these schemes in such cases.
      #
      if {$f == 0} {
        #
        # No penalty for marking a wrong solution, when there is
        # no wrong solution.
        #
        set wi1 [expr {max((100.0/$r) * $rk, 0)}]
        set wi2 [expr {max((100.0/$r) * $rk, 0)}]
      } else {
        set wi1 [expr {max((100.0/$r) * $rk - (100.0/$f) * $fk, 0)}]
        if {$f == 1} {
          #
          # Special rule when there is just one wrong solution.
          #
          set wi2 [expr {max((100.0/$r) * $rk - min(50.0, (100.0/$f)) * $fk, 0)}]
        } else {
          set wi2 $wi1
        }
      }
      set canvas [expr {max(($rk * 100.0/$r) - ($fk * 100.0/$r), 0)}]
      set etk    [expr {100.0 * (($r*1.0+$f) /$r) * ($rk - $fk) / ($R + $W) }]
    } else {
      set wi1 0.0
      set wi2 0.0
      set etk 0.0
      set canvas 0.0
    }
    
    set s1   [expr {100.0 * $R / ($R + $W) }]
    set s2   [expr {100.0 * ($R - $W/2.0) / ($R + $W) }]
    
    set ggw0 [expr {100.0 * ($R - $W) / ($R + $W) }]
    set ggw  [:ggw $R $W]
    
    return [list wi1 $wi1 wi2 $wi2 s1 $s1 s2 $s2 etk $etk ggw0 $ggw0 ggw $ggw canvas $canvas]
  • stats_record_detail (scripted)

    set reporting_obj ::[${:object} parent_id]
    $reporting_obj stats_record_detail -label $label -value $value  -name ${:name}  -correctly_answered $correctly_answered
  • value_if_nothing_is_returned_from_form (scripted)

    
    # Here we have to distinguish between two cases:
    # - edit mode: somebody has removed a mark from a check button;
    #   this means: clear the field
    # - view mode: the fields were deactivated (made insensitive);
    #   this means: keep the old value -> return default
    
    if {[:is_disabled]} {
      return $default
    } else {
      return ""
    }