[system] / trunk / webwork / system / courseScripts / AnswerHash.pm Repository:
ViewVC logotype

View of /trunk/webwork/system/courseScripts/AnswerHash.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 109 - (download) (as text) (annotate)
Thu Aug 9 21:37:29 2001 UTC (11 years, 9 months ago) by gage
File size: 16651 byte(s)
AnswerEvaluators can now handle ans_hash as an input (this allows
the answerevaluator to be used as a filter in another answer evaluator

Cosmetic changes made to the error reporting when the debug
flag is set.

    1 ##########################################################################
    2 ## AnswerHash Package
    3 ##
    4 ## Provides a data structure for answer hashes. Currently just a wrapper
    5 ## for the hash, but that might change
    6 
    7 =pod
    8 
    9 For the most part AnswerHash is an object which contains data.  It has only a few methods.
   10 The data which is automatically initiallized by the constructor new is given here:
   11 
   12     $new_answer_hash       =        {   'score'         =>  0,
   13                       'correct_ans'     =>  "No correct answer specified",
   14                       'student_ans'     =>  undef,
   15                       'original_student_ans', =>  undef,
   16                       'type'          =>  'Undefined answer evaluator type',
   17                       'ans_message'     =>  '',
   18 
   19                       'preview_text_string' =>  undef,
   20                       'preview_latex_string'  =>  undef,
   21                       'error_flag'      =>  undef,
   22                       'error_message'       =>  '',
   23 
   24                     };
   25 
   26 
   27 
   28 Methods:
   29         new
   30 
   31         setKeys       $rh_ans->setKeys{score=>1};  Sets elements in the AnswerHash.
   32                                                            There is a check to make sure that the
   33                                                            key is one of the values listed above.
   34 
   35                             $rh_ans->{non_standard_value} = 'oops';
   36                                             Add an element to the AnswerHash.
   37                                             No checks are made. Can be used (cautiously)
   38                                             to customize and extend the AnswerHash type.
   39 
   40         OR
   41 
   42         AND
   43 
   44 =cut
   45 
   46 BEGIN {
   47   be_strict(); # an alias for use strict.  This means that all global variable must contain main:: as a prefix.
   48 
   49 }
   50 
   51 package AnswerHash;
   52 # initialization fields
   53 my %fields = (    'score'         =>  undef,
   54           'correct_ans'     =>  undef,
   55           'student_ans'     =>  undef,
   56           'ans_message'     =>  undef,
   57           'type'          =>  undef,
   58           'preview_text_string' =>  undef,
   59           'preview_latex_string'  =>  undef,
   60           'original_student_ans'  =>  undef
   61       );
   62 
   63 ## Initializing constructor
   64 sub new {
   65   my $class = shift @_;
   66 
   67   my $self  = { 'score'         =>  0,
   68           'correct_ans'     =>  'No correct answer specified',
   69           'student_ans'     =>  undef,
   70           'ans_message'     =>  '',
   71           'type'          =>  'Undefined answer evaluator type',
   72           'preview_text_string' =>  undef,
   73           'preview_latex_string'  =>  undef,
   74           'original_student_ans'  =>  undef,
   75           'error_flag'      =>  undef,
   76           'error_message'       =>  '',
   77 
   78   };  # return a reference to a hash.
   79 
   80   bless $self, $class;
   81   $self -> setKeys(@_);
   82 
   83   return $self;
   84 }
   85 
   86 ## IN: a hash
   87 ## Checks to make sure that the keys are valid,
   88 ## then sets their value
   89 sub setKeys {
   90     my $self = shift;
   91   my %inits = @_;
   92   foreach my $item (keys %inits) {
   93     if ( exists $fields{$item} ) {
   94       $self -> {$item} = $inits{$item};
   95     }
   96     else {
   97       warn "AnswerHash cannot automatically initialize an item named $item";
   98     }
   99   }
  100 }
  101 
  102 # access methods
  103 sub data {    #$rh_ans->data('foo') is a synonym for $rh_ans->{student_ans}='foo'
  104   my $self = shift;
  105   $self->input(@_);
  106 }
  107 
  108 sub input {     #$rh_ans->input('foo') is a synonym for $rh_ans->{student_ans}='foo'
  109   my $self = shift;
  110     my $input = shift;
  111     $self->{student_ans} = $input if defined($input);
  112   $self->{student_ans}
  113 }
  114 sub score {
  115   my $self = shift;
  116     my $score = shift;
  117     $self->{score} = $score if defined($score);
  118   $self->{score}
  119 }
  120 
  121 # error methods
  122 sub throw_error {
  123   my $self = shift;
  124     my $flag = shift;
  125     my $message = shift;
  126     $self->{error_message} .= " $message " if defined($message);
  127     $self->{error_flag} = $flag if defined($flag);
  128   $self->{error_flag}
  129 }
  130 sub catch_error {
  131   my $self = shift;
  132     my $flag = shift;
  133     return('')  unless defined($self->{error_flag});
  134     return $self->{error_flag} unless $flag;    # empty input catches all errors.
  135     return $self->{error_flag} if $self->{error_flag} eq $flag;
  136   return '';   # nothing to catch
  137 }
  138 sub clear_error {
  139   my $self = shift;
  140   my $flag = shift;
  141   if (defined($flag) and $flag =~/\S/ and defined($self->{error_flag})  and $flag eq $self->{error_flag}) {
  142     $self->{error_flag} = undef;
  143     $self->{error_message} = undef;
  144   }
  145   $self;
  146 }
  147 sub error_flag {
  148   my $self = shift;
  149     my $flag = shift;
  150     $self->{error_flag} = $flag if defined($flag);
  151   $self->{error_flag}
  152 }
  153 sub error_message {
  154   my $self = shift;
  155     my $message = shift;
  156     $self->{error_message} = $message if defined($message);
  157   $self->{error_message}
  158 }
  159 
  160 # error print out method
  161 
  162 sub pretty_print {
  163     my $r_input = shift;
  164     my $out = '';
  165     if ( not ref($r_input) ) {
  166       $out = $r_input;    # not a reference
  167     } elsif (ref($r_input) =~/hash/i) {
  168       local($^W) = 0;
  169     $out .= "<TABLE border = \"2\" cellpadding = \"3\" BGCOLOR = \"#FFFFFF\">";
  170     foreach my $key (sort keys %$r_input ) {
  171       $out .= "<tr><TD> $key</TD><TD>=&gt;</td><td>&nbsp;".pretty_print($r_input->{$key}) . "</td></tr>";
  172     }
  173     $out .="</table>";
  174   } elsif (ref($r_input) eq 'ARRAY' ) {
  175     my @array = @$r_input;
  176     $out .= "( " ;
  177     while (@array) {
  178       $out .= pretty_print(shift @array) . " , ";
  179     }
  180     $out .= " )";
  181   } elsif (ref($r_input) eq 'CODE') {
  182     $out = "$r_input";
  183   } else {
  184     $out = $r_input;
  185   }
  186     $out;
  187 }
  188 
  189 # action methods
  190 sub OR {
  191   my $self = shift;
  192 
  193   my $rh_ans2 = shift;
  194   my %options = @_;
  195   return($self) unless defined($rh_ans2) and ref($rh_ans2) eq 'AnswerHash';
  196 
  197   my $out_hash = new AnswerHash;
  198   # score is the maximum of the two scores
  199   $out_hash->{score} = ( $self->{score}  <  $rh_ans2->{score} ) ? $rh_ans2->{score} :$self->{score};
  200   $out_hash->{correct_ans} = join(" OR ", $self->{correct_ans}, $rh_ans2->{correct_ans} );
  201   $out_hash->{student_ans} = $self->{student_ans};
  202   $out_hash->{type} = join(" OR ", $self->{type}, $rh_ans2->{type} );
  203   $out_hash->{preview_text_string} = join("   ", $self->{preview_text_string}, $rh_ans2->{preview_text_string} );
  204   $out_hash->{original_student_ans} = $self->{original_student_ans};
  205   $out_hash;
  206 }
  207 
  208 sub AND {
  209   my $self = shift;
  210   my $rh_ans2 = shift;
  211   my %options = @_;
  212   my $out_hash = new AnswerHash;
  213   # score is the minimum of the two scores
  214   $out_hash->{score} = ( $self->{score}  >  $rh_ans2->{score} ) ? $rh_ans2->{score} :$self->{score};
  215   $out_hash->{correct_ans} = join(" AND ", $self->{correct_ans}, $rh_ans2->{correct_ans} );
  216   $out_hash->{student_ans} = $self->{student_ans};
  217   $out_hash->{type} = join(" AND ", $self->{type}, $rh_ans2->{type} );
  218   $out_hash->{preview_text_string} = join("   ", $self->{preview_text_string}, $rh_ans2->{preview_text_string} );
  219   $out_hash->{original_student_ans} = $self->{original_student_ans};
  220   $out_hash;
  221 }
  222 
  223 package AnswerEvaluator;
  224 
  225 
  226 
  227 
  228 sub new {
  229   my $class = shift @_;
  230 
  231   my $self  = { pre_filters   =>  [ [\&blank_prefilter] ],
  232           evaluators    =>  [],
  233           post_filters  =>  [ [\&blank_postfilter] ],
  234           debug     =>  0,
  235           rh_ans    =>  new AnswerHash,
  236 
  237   };
  238 
  239   bless $self, $class;
  240   $self->rh_ans(@_);    #initialize answer hash
  241   return $self;
  242 }
  243 
  244 # dereference_array_ans pretty prints an answer which is stored as an anonymous array.
  245 sub dereference_array_ans {
  246   my $self = shift;
  247   my $rh_ans = shift;
  248   if (defined($rh_ans->{student_ans}) and ref($rh_ans->{student_ans}) eq 'ARRAY'  ) {
  249     $rh_ans->{student_ans} = "( ". join(" , ",@{$rh_ans->{student_ans}} ) . " ) ";
  250   }
  251   $rh_ans;
  252 }
  253 
  254 sub get_student_answer {
  255   my $self  = shift;
  256   my $input   = shift;
  257   if (ref($input) =~/AnswerHash/) {
  258     # in this case nothing needs to be done, since the student's answer is already in an answerhash.
  259     # This is useful when an AnswerEvaluator is used as a filter in another answer evaluator.
  260   } elsif ($input =~ /\0/ ) {
  261       my @input = split(/\0/,$input);
  262       $self-> {rh_ans} -> {original_student_ans} = " ( " .join(", ",@input) . " ) ";
  263     $input = \@input;
  264     $self-> {rh_ans} -> {student_ans} = $input;
  265   } else {
  266       $input = '' unless defined($input);
  267     $self-> {rh_ans} -> {original_student_ans} = $input;
  268     $self-> {rh_ans} -> {student_ans} = $input;
  269   }
  270 
  271 
  272   $input;
  273 }
  274 
  275 sub evaluate {
  276   my $self    =   shift;
  277   $self->get_student_answer(shift @_);
  278   my $rh_ans    =   $self ->{rh_ans};
  279     warn "<H3> Answer evaluator information: </H3>\n" if $self->{debug}>0;
  280   my @prefilters  = @{$self -> {pre_filters}};
  281   my $count = -1;  # the blank filter is counted as filter 0
  282   foreach my $i (@prefilters) {
  283       last if defined( $self->{rh_ans}->{error_flag} );
  284       my @array = @$i;
  285       my $filter = shift(@array);      # the array now contains the options for the filter
  286       my %options = @array;
  287       if (defined($self->{debug}) and $self->{debug}>0) {
  288 
  289         $self->{rh_ans}->{rh_options} = \%options;  #include the options in the debug information
  290         warn "before pre-filter: ",++$count, $self->{rh_ans}->pretty_print();
  291       }
  292       $rh_ans   = &$filter($rh_ans,@array);
  293       warn "<h4>Filter Name:", $rh_ans->{_filter_name},"</h4><BR>\n" if $self->{debug}>0 and defined($rh_ans->{_filter_name});
  294       $rh_ans->{_filter_name} = undef;
  295   }
  296   my @evaluators = @{$self -> {evaluators} };
  297   $count = 0;
  298   foreach my $i ( @evaluators )   {
  299       last if defined($self->{rh_ans}->{error_flag});
  300     my @array = @$i;
  301       my $evaluator = shift(@array);   # the array now contains the options for the filter
  302       my %options = @array;
  303       if (defined($self->{debug}) and $self->{debug}>0) {
  304         $self->{rh_ans}->{rh_options} = \%options;  #include the options in the debug information
  305         warn "before evaluator: ",++$count, $self->{rh_ans}->pretty_print();
  306       }
  307     $rh_ans   = &$evaluator($rh_ans,@array);
  308     warn "<h4>Filter Name:", $rh_ans->{_filter_name},"</h4><BR>\n" if $self->{debug}>0 and defined($rh_ans->{_filter_name});
  309     $rh_ans->{_filter_name} = undef;
  310   }
  311   my @post_filters = @{$self -> {post_filters} };
  312   $count = -1;  # blank filter catcher is filter 0
  313   foreach my $i ( @post_filters ) {
  314       last if defined($rh_ans->{done}) and $rh_ans->{done} == 1;    # no further action needed
  315     my @array = @$i;
  316 
  317       my $filter = shift(@array);      # the array now contains the options for the filter
  318       my %options = @array;
  319       if (defined($self->{debug}) and $self->{debug}>0) {
  320         $self->{rh_ans}->{rh_options} = \%options;  #include the options in the debug information
  321         warn "before post-filter: ",++$count, $self->{rh_ans}->pretty_print(),"\n";
  322       }
  323 
  324     $rh_ans   = &$filter($rh_ans,@array);
  325     warn "<h4>Filter Name:", $rh_ans->{_filter_name},"</h4><BR>\n" if $self->{debug}>0 and defined($rh_ans->{_filter_name});
  326     $rh_ans->{_filter_name} = undef;
  327   }
  328   $rh_ans = $self->dereference_array_ans($rh_ans);
  329   # make sure that the student answer is not an array so that it is reported correctly in answer section.
  330   warn "<h4>final result: </h4>", $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0;
  331   $self ->{rh_ans} = $rh_ans;
  332   $rh_ans;
  333 }
  334 # This next subroutine is for checking the instructor's answer and is not yet in use.
  335 sub correct_answer_evaluate {
  336   my $self    =   shift;
  337   $self-> {rh_ans} -> {correct_ans} = shift @_;
  338   my $rh_ans    =   $self ->{rh_ans};
  339   my @prefilters  = @{$self -> {correct_answer_pre_filters}};
  340   my $count = -1;  # the blank filter is counted as filter 0
  341   foreach my $i (@prefilters) {
  342       last if defined( $self->{rh_ans}->{error_flag} );
  343       my @array = @$i;
  344       my $filter = shift(@array);      # the array now contains the options for the filter
  345       warn "before pre-filter: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0;
  346     $rh_ans   = &$filter($rh_ans,@array);
  347     warn "Filter Name:", $rh_ans->{_filter_name},"<BR>\n" if $self->{debug}>0 and defined($rh_ans->{_filter_name})
  348   }
  349   my @evaluators = @{$self -> {correct_answer_evaluators} };
  350   $count = 0;
  351   foreach my $i ( @evaluators )   {
  352       last if defined($self->{rh_ans}->{error_flag});
  353     my @array = @$i;
  354       my $evaluator = shift(@array);   # the array now contains the options for the filter
  355       warn "before evaluator: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0;
  356     $rh_ans   = &$evaluator($rh_ans,@array);
  357   }
  358   my @post_filters = @{$self -> {correct_answer_post_filters} };
  359   $count = -1;  # blank filter catcher is filter 0
  360   foreach my $i ( @post_filters ) {
  361       last if defined($rh_ans->{done}) and $rh_ans->{done} == 1;    # no further action needed
  362     my @array = @$i;
  363       my $filter = shift(@array);      # the array now contains the options for the filter
  364       warn "before post-filter: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0;
  365     $rh_ans   = &$filter($rh_ans,@array);
  366     warn "Filter Name:", $rh_ans->{_filter_name},"<BR>\n" if $self->{debug}>0 and defined($rh_ans->{_filter_name})
  367   }
  368   $rh_ans = $self->dereference_array_ans($rh_ans);
  369   # make sure that the student answer is not an array so that it is reported correctly in answer section.
  370   warn "final result: ", $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0;
  371   $self ->{rh_ans} = $rh_ans;
  372   $rh_ans;
  373 }
  374 
  375 sub install_pre_filter {
  376   my $self =  shift;
  377   if (@_ == 0) {
  378     # do nothing if input is empty
  379   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  380     $self->{pre_filters} = [];
  381   } else {
  382     push(@{$self->{pre_filters}},[ @_ ]) if @_;  #install pre_filter and it's options
  383   }
  384   @{$self->{pre_filters}};  # return array of all pre_filters
  385 }
  386 
  387 sub install_evaluator {
  388   my $self =  shift;
  389   if (@_ == 0) {
  390     # do nothing if input is empty
  391   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  392     $self->{evaluators} = [];
  393   } else {
  394     push(@{$self->{evaluators}},[ @_ ]) if @_; #install evaluator and it's options
  395   }
  396   @{$self->{'evaluators'}};  # return array of all evaluators
  397 }
  398 
  399 sub install_post_filter {
  400   my $self =  shift;
  401   if (@_ == 0) {
  402     # do nothing if input is empty
  403   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  404     $self->{post_filters} = [];
  405   } else {
  406     push(@{$self->{post_filters}}, [ @_ ]) if @_; #install post_filter and it's options
  407   }
  408   @{$self->{post_filters}};  # return array of all post_filters
  409 }
  410 
  411 ## filters for checking the correctAnswer
  412 sub install_correct_answer_pre_filter {
  413   my $self =  shift;
  414   if (@_ == 0) {
  415     # do nothing if input is empty
  416   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  417     $self->{correct_answer_pre_filters} = [];
  418   } else {
  419     push(@{$self->{correct_answer_pre_filters}},[ @_ ]) if @_;  #install correct_answer_pre_filter and it's options
  420   }
  421   @{$self->{correct_answer_pre_filters}};  # return array of all correct_answer_pre_filters
  422 }
  423 
  424 sub install_correct_answer_evaluator {
  425   my $self =  shift;
  426   if (@_ == 0) {
  427     # do nothing if input is empty
  428   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  429     $self->{correct_answer_evaluators} = [];
  430   } else {
  431     push(@{$self->{correct_answer_evaluators}},[ @_ ]) if @_; #install evaluator and it's options
  432   }
  433   @{$self->{correct_answer_evaluators}};  # return array of all evaluators
  434 }
  435 
  436 sub install_correct_answer_post_filter {
  437   my $self =  shift;
  438   if (@_ == 0) {
  439     # do nothing if input is empty
  440   } elsif ($_[0] eq 'reset' or $_[0] eq 'erase' ) {
  441     $self->{correct_answer_post_filters} = [];
  442   } else {
  443     push(@{$self->{correct_answer_post_filters}}, [ @_ ]) if @_; #install post_filter and it's options
  444   }
  445   @{$self->{correct_answer_post_filters}};  # return array of all post_filters
  446 }
  447 
  448 sub ans_hash {  #alias for rh_ans
  449   my $self = shift;
  450   $self->rh_ans(@_);
  451 }
  452 sub rh_ans {
  453   my $self = shift;
  454   my %in_hash = @_;
  455   foreach my $key (keys %in_hash) {
  456     $self->{rh_ans}->{$key} = $in_hash{$key};
  457   }
  458   $self->{rh_ans};
  459 }
  460 ######################################################
  461 #
  462 # Built in Filters
  463 #
  464 ######################################################
  465 
  466 
  467 sub blank_prefilter  { # check for blanks
  468   my $rh_ans = shift;
  469     # undefined answers are BLANKS
  470   ( not defined($rh_ans->{student_ans}) ) && do {$rh_ans->throw_error("BLANK", 'The answer is blank');
  471                             return($rh_ans);};
  472     # answers which are arrays or hashes or some other object reference  are NOT blanks
  473     ( ref($rh_ans->{student_ans} )        ) && do { return( $rh_ans ) };
  474     # if the answer is a true variable consisting only of white space it is a BLANK
  475     ( ($rh_ans->{student_ans}) !~ /\S/   )    && do {$rh_ans->throw_error("BLANK", 'The answer is blank');
  476                             return($rh_ans);};
  477   # If we get to here, we assume that the answer is not a blank. It is defined, not a reference
  478   # and contains something other than whitespaces.
  479   $rh_ans;
  480 };
  481 
  482 sub blank_postfilter  {
  483   my $rh_ans=shift;
  484     return($rh_ans) unless defined($rh_ans->{error_flag}) and $rh_ans->{error_flag} eq 'BLANK';
  485     $rh_ans->{error_flag} = undef;
  486     $rh_ans->{error_message} = '';
  487     $rh_ans->{done} =1;    # no further checking is needed.
  488     $rh_ans;
  489 };
  490 
  491 1;
  492 #package AnswerEvaluatorMaker;
  493 

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9