CAT2p4chem.pm

This package contains utility programs used (and abandoned) by several generations of questions on this site (freshman chemistry), the pchem site, and the advanced graduate student site.

package CAT2p4pchem;
#extended to return  true if qnd is good answer, for use
# in chem prelab multiquestion mode.
# April 10, 2000 adding one and two digit integer answer
#     and changing qnd so that
#     1) 2 sig figs possible, x.yE etc., DONE
#     2) answer judged as a percent????
#     3) power of ten warning given
#WORK started (IV/10/2000)
# adding full reporting capabilities, may 2000
#above WORK ended (DATE)
use CGI ':standard';
use CGI::Carp qw(fatalsToBrowser);
#use diagnostics;
use Text::Wrap;


require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(send_memo rno round qnd mc time_stamp save_vars exam_qnd 
  our_time text_in txt_in login_report report comment1 help qxd);
sub new{
  my $this = {}; #create anonymous hash and #self points to it
  bless $this; # connect the hash to CATutils
  
  return $this;#return the hash reference
}

A random number generator:

sub rno {
#
# returns a random number between 0 and 1, with $_[0] digits to the right
# of the decimal point
#
  shift;
  return (10**$_[0]*rand() % 10**$_[0])/(10**$_[0]);
}

A rounding subroutine


sub round {
#
# rounds first argument to length of second
#
  shift;
  return(int($_[0]*10**$_[1])/10**$_[1]);
}

A "help" subroutine which prints textual help, and records the student's having asked for help.

sub help{
  my ($query) = $_[1];# this seems to work only in the newer version of CGI.pm
  my $var = $_[2];# this is changed in the calling routine, to provide a unique
#	name for variables in this subroutine, where they are concatenated
  my $text = $_[3];# text to be written
  my $filename = $_[4];#student_file text, which might be a URL
  print $text;
  $tempfile = ">>../".$filename.".dat";
  &report("HELP  ".$question.":".$var,$stu_ans,$ans,$tempfile);
}#end of sub help

iSubroutine examination query: exam_qnd.


sub exam_qnd
# calling sequence, required!!!!!
{
  my ($query) = $_[1];# this seems to work only in the newer version of CGI.pm
  my $var = $_[2];# this is changed in the calling routine, to provide a unique
#	name for variables in this subroutine, where they are concatenated
  my $ans = $_[3];# this the answer to be judged
  my $n = $_[4];#q2d or q3d?
#	n=1 means one digit after decimal point,
#	n=2 means two digits after decimal point,
#	n=3 means three digits, anything else
#			is nonesense
  my  $filename  = $_[5];#student_file text, which might be a URL
# this is where we will ultimately write out the record for this student
#this is where we have a flag for future use
  my $flag = $_[6];
# if flag = 1 not set yet, left for future possibilities
  
  
  my $debug = 'false';
  
  $question = $query->script_name();
  $vv1 = rindex($question,"\/");
  $len = length($question);
  $question = substr($question,$vv1+1,$len-$vv1).localtime();#remove directory
  if ($debug eq "true"){print "<br>question(1) = $question <br>";}
  
  $tempfile = ">>../".$filename.".dat";
  
  my($power_0,$s10,$s20,$xa0,$ya0,$za0,$za01,$t100,$t10,$stu_ans,$v_1,$v_2);
  print "<P> Choose your answer in scientific notation,
  e.g., ±1.000x<font color=red>10<sup>±00</sup></font><br>",
  $query->scrolling_list($var.'s10',
  ['+','-'],'+'),
  $query->popup_menu($var.'xa0',
  ['1','2','3','4','5','6','7','8','9'],'1'),
  "<STRONG>.</STRONG> ",
  $query->popup_menu($var.'ya0',
  ['0','1','2','3','4','5','6','7','8','9'],'0');
  if ($n >=2){print
    $query->popup_menu($var.'za0',
  ['0','1','2','3','4','5','6','7','8','9'],'0');}
  if ($n == 3){print
    $query->popup_menu($var.'za01',
  ['0','1','2','3','4','5','6','7','8','9'],'0')};
  
  
  print " x 10<sup>{</sup>";
    print $query->scrolling_list($var.'s20',
    ['+','-',],'+'),
    $query->popup_menu($var.'t100',
    ['0','1','2','3','4','5','6','7','8','9'],'0'),
    $query->popup_menu($var.'t10',
    ['0','1','2','3','4','5','6','7','8','9'],'0');
  print "<sup>}</sup>";
#print "<br> n = $n";
  if ($query->param($var.'xa0')) {
    $xa0 = $query->param($var.'xa0');
    $ya0 = $query->param($var.'ya0');
    $za0 = $query->param($var.'za0');
    $za01 = $query->param($var.'za01');
    
    if($n == 3){$stu_ans = $xa0+(100*$ya0+10*$za0+$za01)/1000}
    elsif($n == 2)
    {$stu_ans = $xa0+(10*$ya0+$za0)/100}
    elsif($n == 1) {
    $stu_ans = $xa0+($ya0)/10}
    else{
      $stu_ans = $xa0+(100*$ya0+10*$za0+$za01)/1000
    }
    if ($debug eq "true"){
      print <<EOF;
      <br> xa0 = $xa0
      <br> ya0 = $ya0
      <br> za0 = $za0
      <br>za01 = $za01
      <br> student constructed $stu_ans = $xa0+(10*$ya0+$za0)/100
      <br>stu_ans = $stu_ans
EOF
    }
    $s10 = $query->param($var.'s10');
    $s20 = $query->param($var.'s20');
    if ($s10 eq "-"){$stu_ans = - $stu_ans};
    $t10 = $query->param($var.'t10');
    $t100 = $query->param($var.'t100');
    $power_0 = $t100*10+$t10;
    if ($s20 eq "-"){$power_0 = - $power_0};
    $stu_ans = $stu_ans * (10 ** $power_0);
    $mantissa_ans = $ans*10**(-$power_0);
    $mantissa_stu_ans = $stu_ans/(10**$power_0);
    $v_1 = abs(($stu_ans - $ans)  );#absolute error
    
    if($ans<0){#looking to find the last digit which might be off by one
      $v1 = substr($ans,0,$n+3)
      } else {
      $v1 = substr($ans,0,$n+2)
    }
    
#print "<br> truncated answer = ",$v1;
    $v_mantissa = abs(($mantissa_stu_ans - $mantissa_ans)  );#absolute error
    $v_2 = abs($v_mantissa);#absolute error scaled.
    if ($n == 2){$crit = .009}
    elsif ($n == 1){$crit = .09}
    else{$crit = 0.001}
    if($debug eq "true"){
      print <<EOF;
      <br> mantissa_ans = $mantissa_ans
      <br> mantissa_stu_ans = $mantissa_stu_ans
      <br> v_1 = $v_1
      <br> v_mantissa = $v_mantissa
      <br> v_2 = $v_2
      <br> criterion = $crit
      <br> ans = $ans
      <br> stu_ans = $stu_ans
EOF
    }
#print "</sub>";
    if ($v_2 < $crit){
      print " <font color=green>was <EM>Right! </EM>.
      <IMG SRC=../icons/check.gif><font color=black>";
      
      &report("RIGHT ".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      return 1;
    }
    elsif ($v_2 < 2*$crit) {
      print "<blink> might be <EM>Right,</EM> i.e., it appears close to the
      expected answer.</blink>";
      &report("APPROX".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      
      return 0 if ($flag == 1);
    }
    elsif(abs($stu_ans/$ans) <= 1/10 ||
      abs($stu_ans/$ans) >= 10){
      print "<blink><br>It would appear that there is a power of ten
      discrepency between your answer and the expected answer.</blink>";
      &report("PowTen".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      return 0 if ($flag == 1);
    }
    elsif ($stu_ans*$ans <0){
      print "<br> student answer = ",$stu_ans;
      print "<br> answer = ",$ans;
      &report("SIGN  ".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      print "<blink> differs in sign from the expected answer.</blink>";
      return 0 if ($flag == 1);
    }
    else{
#here us the point at which to check for help (above)
      for ($i = 0 ; $i <=$eell ; $i++){
        $ourans = $wrong[$i];
        $ouranseval = eval($ourans);
        $mantissa_ans = $ouranseval*10**(-$power_0);
        $mantissa_stu_ans = $stu_ans/(10**$power_0);
        print "our ans = $ourans and evaluated = $ouranseval";
        $v_1 = abs(($stu_ans - $ans)  );#absolute error
        $v_mantissa = abs(($mantissa_stu_ans - $mantissa_ans)  );#absolute error
        $v_2 = abs($v_mantissa);#absolute error scaled.
#	print "<br>Your answer was recorded as ",$stu_ans,".";
#	print "<br>$ouranseval and $stu_ans  , $test";
#	print "<br>crit , $crit";
        if ($v_2 < $crit){
        print "<blink><font color=red><br>  $response[$i]  </blink></font>";}
      }
      print "<br>Your answer, $stu_ans ,  <font color=red> appears to be <EM>Wrong! </EM>.
      <IMG SRC=../icons/checkno.gif><font color=black>";
      &report("WRONG ".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      return 0 if ($flag == 1);
    }
  };#end of if($query->param($var.'xa0'))
  
};#end of exam_qnd

Subroutine qnd which is the main query for a numerical answer, in this case, with 3 significant figures.

sub qnd
# Dec 2000 changed return to be 0 if OK, else 1 
# calling sequence, required!!!!!
{
  my ($query) = $_[1];# this seems to work only in the newer version of CGI.pm
  my $var = $_[2];# this is changed in the calling routine, to provide a unique
#	name for variables in this subroutine, where they are concatenated
  my $ans = $_[3];# this the answer to be judged
  my $n = $_[4];#q2d or q3d?
#	n=1 means one digit after decimal point,
#	n=2 means two digits after decimal point,
#	n=3 means three digits, anything else
#			is nonesense
  my  $filename  = $_[5];#student_file text, which might be a URL
# this is where we will ultimately write out the record for this student
#this is where we start the new material, to add lists of errors and
# error responses
  my $addressOfList;# = $_[6];
#  $el = 0;
#  $eell = 1;
#  until (@$addressOfList[$el] == ""){
#    $wrong[$eell] = @$addressOfList[$el];
#    $response[$eell] = @$addressOfList[$el+1];
#    $el = $el +2;
#    $eell++;
#  }
#  $eell--;#count of number of error responses given by teacher.
  
  
#  my $debug = 'false';
my  $debug = 'false';
  
  $question = $query->script_name();
  $vv1 = rindex($question,"\/");
  $len = length($question);
  $t = localtime();
  if($debug eq "true"){
    print "<br> vv1 = $vv1 and length = ",length($question);
    print "<br> time = $t";
    print "<br> answer regarded as correct = $ans ";
  }
  $question = substr($question,$vv1+1,$len-$vv1)."-".localtime();#this chops off /cgi...
 if ($debug eq "true"){print "<br>question = $question <br>";}
  
  $tempfile = ">>../".$filename.".std";
  
  my($power_0,$s10,$s20,$xa0,$ya0,$za0,$za01,$t100,$t10,$stu_ans,$v_1,$v_2);
  print "<P> Choose your answer in scientific notation,
  e.g., ±1.000x<font color=red>10<sup>±00</sup></font><br>",
  $query->scrolling_list($var.'s10',
  ['+','-'],'+'),
  $query->popup_menu($var.'xa0',
  ['1','2','3','4','5','6','7','8','9'],'1'),
  "<STRONG>.</STRONG> ",
  $query->popup_menu($var.'ya0',
  ['0','1','2','3','4','5','6','7','8','9'],'0');
  if ($n >=2){print
    $query->popup_menu($var.'za0',
  ['0','1','2','3','4','5','6','7','8','9'],'0');}
  if ($n == 3){print
    $query->popup_menu($var.'za01',
  ['0','1','2','3','4','5','6','7','8','9'],'0')};
  
  
  print "<font color=red size=+1><STRONG> x 10</STRONG><sup>{ </font>",
    $query->scrolling_list($var.'s20',
    ['+','-',],'+'),
    $query->popup_menu($var.'t100',
    ['0','1','2','3','4','5','6','7','8','9'],'0'),
    $query->popup_menu($var.'t10',
    ['0','1','2','3','4','5','6','7','8','9'],'0'),
  "<font color=red><STRONG>}</STRONG></font></sup></font>";
  if ($query->param($var.'xa0')) {
    $xa0 = $query->param($var.'xa0');
    $ya0 = $query->param($var.'ya0');
    if($n >=2){
    $za0 = $query->param($var.'za0');}
    if($n == 3){$za01 = $query->param($var.'za01')};
    
    if($n == 3){$stu_ans = $xa0+(100*$ya0+10*$za0+$za01)/1000}
    elsif($n == 2)
    {$stu_ans = $xa0+(10*$ya0+$za0)/100}
    else {
    $stu_ans = $xa0+($ya0)/10}
    if ($debug eq "true"){
      print <<EOF;
      <br> xa0 = $xa0
      <br> ya0 = $ya0
      <br> za0 = $za0
      <br>za01 = $za01
      <br> student constructed $stu_ans = $xa0+(10*$ya0+$za0)/100
      <br>stu_ans = $stu_ans
EOF
    }
    $s10 = $query->param($var.'s10');
    $s20 = $query->param($var.'s20');
    if ($s10 eq "-"){$stu_ans = - $stu_ans};
    $t10 = $query->param($var.'t10');
    $t100 = $query->param($var.'t100');
    $power_0 = $t100*10+$t10;
    if ($s20 eq "-"){$power_0 = - $power_0};
    $stu_ans = $stu_ans * (10 ** $power_0);
    $mantissa_ans = $ans*10**(-$power_0);
    $mantissa_stu_ans = $stu_ans/(10**$power_0);
    $v_1 = abs(($stu_ans - $ans)  );#absolute error
    
    if($ans<0){#looking to find the last digit which might be off by one
      $v1 = substr($ans,0,$n+3)
      } else {
      $v1 = substr($ans,0,$n+2)
    }
    
#print "<br> truncated answer = ",$v1;
    $v_mantissa = abs(($mantissa_stu_ans - $mantissa_ans)  );#absolute error
    $v_2 = abs($v_mantissa);#absolute error scaled.
    if ($n == 2){$crit = .009}
    elsif ($n == 1){$crit = .09}
    else{$crit = 0.001}
    if($debug eq "true"){
      print <<EOF;
      <br> mantissa_ans = $mantissa_ans
      <br> mantissa_stu_ans = $mantissa_stu_ans
      <br> v_1 = $v_1
      <br> v_mantissa = $v_mantissa
      <br> v_2 = $v_2
      <br> criterion = $crit
      <br> ans = $ans
      <br> stu_ans = $stu_ans
EOF
    }
    if ($v_2 < $crit){
      print " <font color=green>was <EM>Right! </EM>.
      <IMG SRC=../icons/check.gif><font color=black>";
      
      &report("RIGHT ".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      return 1;
    }
    elsif ($v_2 < 2*$crit) {
      $percent = round(abs((($stu_ans-$ans)/$ans)*100),2);
      print "<blink> might be <EM>Right,</EM> i.e., it appears close 
( within about  $percent \% ) of the
      expected answer .</blink>";
      &report("APPROX".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      return 0;
    }
    elsif(abs($stu_ans/$ans) <= 1/10 ||
      abs($stu_ans/$ans) >= 10){
      print "<blink><br>It would appear that there is a power of ten
      discrepency between your answer and the expected answer.</blink>";
      &report("PowTen".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      return 0;
    }
    elsif ($stu_ans*$ans <0){
      print "<br> student answer = ",$stu_ans;
      print "<br> answer = ",$ans;
      &report("SIGN  ".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      print "<blink> differs in sign from the expected answer.</blink>";
      return 0;
    }
    else{
#here us the point at which to check for help (above)
      for ($i = 0 ; $i <=$eell ; $i++){
        $ourans = $wrong[$i];
        $ouranseval = eval($ourans);
        $mantissa_ans = $ouranseval*10**(-$power_0);
        $mantissa_stu_ans = $stu_ans/(10**$power_0);
        $v_1 = abs(($stu_ans - $ans)  );#absolute error
        $v_mantissa = abs(($mantissa_stu_ans - $mantissa_ans)  );#absolute error
        $v_2 = abs($v_mantissa);#absolute error scaled.
        if ($debug eq "true"){
          print "our ans = $ourans and evaluated = $ouranseval";
          print "<br>Your answer was recorded as ",$stu_ans,".";
          print "<br>$ouranseval and ", $stu_ans  , $test ;
        }
        print "<br>crit , $crit" if ($debug eq "true");
        if ($v_2 < $crit){
        print "<blink><font color=red><br>  $response[$i]  </blink></font>";}
      }
      print "<br>Your answer, $stu_ans ,  <font color=red> appears to be <EM>Wrong! </EM>.
      <IMG SRC=../icons/checkno.gif><font color=black>";
      &report("WRONG ".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      if($query->param($var.'Give_The_Answer') eq 'on'){
        print "<br>The expected answer is ".
        &round_up_to_exponent($ans,$n);
        &report("GIVEN ".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      }
      print "<br>If you want to see the expected answer, check here and resubmit";
      print $query->checkbox(-name=>$var.'Give_The_Answer',,,-label=>'');
      if ($help ne ""){
        print $help;# this is the one line help thing
      }
      return 0;
    }
  };#end of if($query->param($var.'xa0'))
  
};#end of qnd


qxd


sub qxd
# Dec 2000 changed return to be 0 if OK, else 1 
# calling sequence, required!!!!!
{
  my ($query) = $_[1];# this seems to work only in the newer version of CGI.pm
  my $var = $_[2];# this is changed in the calling routine, to provide a unique
#	name for variables in this subroutine, where they are concatenated
  my $ans = $_[3];# this the answer to be judged
  my $n = $_[4];#q2d or q3d?
#	n=1 means one digit after decimal point,
#	n=2 means two digits after decimal point,
#	n=3 means three digits, anything else
#			is nonesense
  my  $filename  = $_[5];#student_file text, which might be a URL
# this is where we will ultimately write out the record for this student
#this is where we start the new material, to add lists of errors and
# error responses
  my $addressOfList;# = $_[6];
#  $el = 0;
#  $eell = 1;
#  until (@$addressOfList[$el] == ""){
#    $wrong[$eell] = @$addressOfList[$el];
#    $response[$eell] = @$addressOfList[$el+1];
#    $el = $el +2;
#    $eell++;
#  }
#  $eell--;#count of number of error responses given by teacher.
  
  
  my $debug = 'false';
  $debug = 'true';
  
  $question = $query->script_name();
  $vv1 = rindex($question,"\/");
  $len = length($question);
  $t = localtime();
#  if($debug eq "true"){
#    print "<br> vv1 = $vv1 and length = ",length($question);
#    print "<br> time = $t";
#  }
  $question = substr($question,$vv1+1,$len-$vv1)."-".localtime();#this chops off /cgi...
 if ($debug eq "true"){print "<br>question = $question <br>";}
  
  $tempfile = ">>../".$filename.".std";
 print "<p>Enter your answer here:<font size=+1>",
	$query->textfield($var.'1','',10,30),
	"* 10<sup><sup>",
	$query->textfield($var.'2','',3,3),
	"</sup></sup><p></font>"; 
  my($power_0,$s10,$s20,$xa0,$ya0,$za0,$za01,$t100,$t10,$stu_ans,$v_1,$v_2);
  if ($query->param($var.'1')) {
	$xa0 = $query->param($var.'1');
	$t10 = $query->param($var.'2');
    $stu_ans = $xa0*10**($t10);
    if ($debug eq "true"){
      print <<EOF;
      <br> xa0 = $xa0
	<br> t10 = $t10
	<br> n = $n
EOF
    }
#    $t10 = $query->param($var.'t10');#use for power of ten????
#    $t100 = $query->param($var.'t100');
#    $power_0 = $t100*10+$t10;
#    if ($s20 eq "-"){$power_0 = - $power_0};
#    $stu_ans = $stu_ans * (10 ** $power_0);
#    $mantissa_ans = $ans*10**(-$power_0);
#    $mantissa_stu_ans = $stu_ans/(10**$power_0);
    
      $crit = abs((($stu_ans-$ans)/$ans)*(10**$n));
      $percent = round(abs((($stu_ans-$ans)/$ans)*100),2);
	my $variance = 1/$n;
    if ($debug eq "true"){
      print <<EOF;
      <br> variance allowed for n=$n  = 1/$n = $variance
      <br>crit = $crit
      <br>stu_ans = $stu_ans
      <br>ans = $ans
EOF
}
    if ($crit < $variance){#
      print " <font color=green>was <EM>Right! </EM>.
      <IMG SRC=../icons/check.gif><font color=black>";
      
      &report("RIGHT ".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      return 0;
    }
    elsif ($crit < 2*$variance) {
#remove      $percent = round(abs((($stu_ans-$ans)/$ans)*100),2);
      print "<blink> might be <EM>Right,</EM> i.e., it appears close
(i.e., within ~ $percent % of the expected answer).
      </blink>";
      &report("APPROX".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      return 1;
    }
    elsif(abs($stu_ans/$ans) <= 1/10 ||
      abs($stu_ans/$ans) >= 10){
      print "<blink><br>It would appear that there is a power of ten
      discrepency between your answer and the expected answer.</blink>";
      &report("PowTen".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      return 1;
    }
    elsif ($stu_ans*$ans <0){
      print "<br> student answer = ",$stu_ans;
      print "<br> answer = ",$ans;
      &report("SIGN  ".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      print "<blink> differs in sign from the expected answer.</blink>";
      return 1;
    }
    else{
#here us the point at which to check for help (above)
      for ($i = 0 ; $i <=$eell ; $i++){
        $ourans = $wrong[$i];
        $ouranseval = eval($ourans);
        $mantissa_ans = $ouranseval*10**(-$power_0);
        $mantissa_stu_ans = $stu_ans/(10**$power_0);
        $v_1 = abs(($stu_ans - $ans)  );#absolute error
        $v_mantissa = abs(($mantissa_stu_ans - $mantissa_ans)  );#absolute error
        $v_2 = abs($v_mantissa);#absolute error scaled.
        if ($debug eq "true"){
          print "our ans = $ourans and evaluated = $ouranseval";
          print "<br>Your answer was recorded as ",$stu_ans,".";
          print "<br>$ouranseval and ", $stu_ans  , $test ;
        }
        print "<br>crit , $crit" if ($debug eq "true");
        if ($v_2 < $crit){
        print "<blink><font color=red><br>  $response[$i]  </blink></font>";}
      }
      print "<br>Your answer, $stu_ans ,  <font color=red> appears to be <EM>Wrong! </EM>.
      <IMG SRC=../icons/checkno.gif><font color=black>";
      &report("WRONG ".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      if($query->param($var.'Give_The_Answer') eq 'on'){
        print "<br>The expected answer is ".
        &round_up_to_exponent($ans,$n);
        &report("GIVEN ".$question." QND:".$var,$stu_ans,$ans,$tempfile);
      }
      print "<br>If you want to see the expected answer, check here and resubmit";
      print $query->checkbox(-name=>$var.'Give_The_Answer',,,-label=>'');
      if ($help ne ""){
        print $help;# this is the one line help thing
      }
      return 1;
    }
  };#end of if($query->param($var.'xa0'))
  
};#end of qxd

Subroutine mc, a multiple choice query.

sub mc
# calling sequence, required!!!!!
{
  my ($query) = $_[1];# this seems to work only in the newer version of CGI.pm
  my $var = $_[2];# this is changed in the calling routine, to provide a unique
#			name for variables in this subroutine, where they are concatenated
  my $number = $_[3];# this the number of answers in the choice
  my $n = $_[4];# this is the number of the correct answer
  my $filename = $_[5];
  $tempfile = ">>../".$filename.".dat";
  $question = $query->script_name();
  $vv1 = rindex($question,"\/");
  $len = length($question);
  $question = substr($question,$vv1+1,$len-$vv1).localtime();#this chops off voh/cgi...
  my $h1 = $_[6];#this is the first answer choice
  my $h2 = $_[7];#this is the second answer choice
  my $h3 = $_[8];#this is the third answer choice
  my $h4 = $_[9];#this is the fourth answer choice
  my $h5 = $_[10];#this is the fifth answer choice
  my $h6 = $_[11];#this is the sixth answer choice
  my $debug = 'false';
#print "in mc, number  = ",$number;
#my $n;
  if($number eq '5'){
    print "<P>", $query->scrolling_list($var,
  [$h1,$h2,$h3,$h4,$h5],' ');}
  elsif($number eq '4'){
    print "<P>", $query->scrolling_list($var,
  [$h1,$h2,$h3,$h4],' ');}
  elsif($number eq '3'){
    print "<P>", $query->scrolling_list($var,
  [$h1,$h2,$h3],' ');}
  elsif($number eq '2'){
    print "<P>", $query->scrolling_list($var,
  [$h1,$h2],' ');}
  else{
    print "<P>", $query->scrolling_list($var,
  [$h1,$h2,$h3,$h4,$h5,$h6],' ');}
  if ($query->param($var) ne '') {
    $xa0 = $query->param($var);
    $v = 5+$_[4];#changed April 20, 2000
    if($xa0 eq $_[$v]){
      print " <font color=green>was <EM>Right! </EM>.
      <IMG SRC=../icons/check.gif><font color=black>";
      #&report("RIGHT ".$question."(MC):".$var,$v,$xa0,$tempfile);
      return 1;
    }
    else {
      print " <font color=red>appears to be <EM>Wrong! </EM>.
      <IMG SRC=../icons/checkno.gif><font color=black>";
      #&report("WRONG ".$question."(MC):".$var,$v,$xa0,$temfile);
      return 0;
    }
  }
}


Subroutine comment elicits a response from the student. I would not use this anymore, since mailing is always a problem, and opening up your web site to unrestricted text brings out the worst in some people.


sub comment1{
  my ($query) = $_[1];# this seems to work only in the newer version of CGI.pm
  my $var = $_[2];# this is changed in the calling routine, to provide a unique
  my $filename = $_[3];
  if ($filename eq ""){print "<br><font color=red size=+1>You must enter two names in the first two fields of this examination!</font>";
  }
  $tempfile = ">>../".$filename.".dat";
  print "<br>You may comment on this question here if you wish:<br>";
  print $query->textarea($var,'',3,60);
  if ($query->param($var) ne ""){
    $comment = $query->param($var);
    open (OUTFILE, $tempfile);
    print ( OUTFILE wrap("\t","","student comment on question ",$var," = ",$comment,"\n"));
    print  wrap ("\t","","<br>student comment on question ",$filename," = ",$comment,"<br>");
    close OUTFILE ;
    return $comment;
  }
}#end of comment

login_report

sub login_report{
#print "<br>",@_;
  my $query = $_[0];
  my $rightWrong = $_[2];#login message
  my $answer = $_[3];
  my $correct = $_[4];
  my $filename = $_[5];#this was called using a value of tempfile
  my $debug = "false";
  if($debug eq "true"){
    print "<br>in report (0), $query";
    print "<br>in report (2), $rightWrong";
    print "<br>in report (3),answer  $answer";
    print "<br>in report (4),correct  $correct";
    print "<br>in report (5), $filename";
    print "<br>Writing file $filename<br>";
  }
  open (OUTFILE, ">>".$filename);
  print   OUTFILE $rightWrong," date: ",&my_time,
  "\n" ;
  close OUTFILE ;
}

report

#
sub report{
  my $rightWrong = $_[0];
  my $answer = $_[1];
  my $correct = $_[2];
  my $filename = $_[3];#this was called using a value of tempfile
# last argument not used. tempfile passed in common
  my $debug = "false";
  if($debug eq "true"){
    print "<br>in report (0), $_[0]";
    print "<br>in report (1), $_[1]";
    print "<br>in report (2), $_[2]";
    print "<br>in report (3), $_[3]";
    print "<br>Writing file $tempfile<br>";
  }
  open (OUTFILE, $tempfile);
  print  OUTFILE $rightWrong," stud: ",$answer,
  " ans: ",$correct,"\n";
  close OUTFILE ;
}
#

our_time

sub our_time{
  %weekday = ("0","Sunday",
    "1","Monday", "2","Tuesday", "3","Wednesday", "4","Thursday",
  "5","Friday", "6","Saturday");
  %MONTH = ("0","January",
    "1","February", "2","March", "3","April", "4","May",
    "5","June", "6","July", "7","August", "8","September",
  "9","October", "10","November", "11","December");
  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
  print "<br>  ", $hour,":",$min,":",$sec,": ",$weekday{$wday},", ",$MONTH{$mon}," ",$mday,", ",1900+$year;
}#end of our_time


my_time

sub my_time{
  %weekday = ("0","Sunday",
    "1","Monday", "2","Tuesday", "3","Wednesday", "4","Thursday",
  "5","Friday", "6","Saturday");
  %MONTH = ("0","January",
    "1","February", "2","March", "3","April", "4","May",
    "5","June", "6","July", "7","August", "8","September",
  "9","October", "10","November", "11","December");
  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
  return($hour,":",$min,":",$sec,": ",$weekday{$wday},", ",$MONTH{$mon}," ",$mday,", ",1900+$year);
}#end of our_time



time_stamp

#
sub time_stamp{
  my ($query) = $_[1];# this seems to work only in the newer version of CGI.pm
  my ($filename) = $_[2];# this seems to work only in the newer version of CGI.pm
  
  ($device,$inode,$mode,$nblink,$quid,$gid,$rdev,$size,$atime,
  $mtime,$ctime,$blksize,$blocks) = stat($filename);
  %weekday = ("0","Sunday",
    "1","Monday",
    "2","Tuesday",
    "3","Wednesday",
    "4","Thursday",
    "5","Friday",
  "6","Saturday");
  %MONTH = ("0","January",
    "1","February",
    "2","March",
    "3","April",
    "4","May",
    "5","June",
    "6","July",
    "7","August",
    "8","September",
    "9","October",
    "10","November",
  "11","December");
  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($mtime);
#  if ($year le 50){$pre = "20"} else {$pre = "19"};
  print "The last time this file was changed (edited) was  ", $weekday{$wday},", ",$MONTH{$mon}," ",$mday;
# there is a problem with the year, so the hell with it, Jan 2000
}#end of time_stamp




Subroutine text_in allows students to answer with text. Again, I would not use this any more.


sub text_in{
  my ($query) = $_[1];# this seems to work only in the newer version of CGI.pm
  my $var = $_[2];# this is changed in the calling routine, to provide a unique
#			name for variables in this subroutine, where they are concatenated
  my $ans = $_[3];# this the answer to be judged
  my $n = $_[4];#number of characters in input field, note that 10*this number = total length
  my $student_file = $_[5];#student_file text, which might be a URL
  my $debug = 'false';
  $tempfile = ">>../".$filename.".dat";
  $question = $query->script_name();
  $vv1 = rindex($question,"\/");
  $len = length($question);
  print "<br>",$query->textfield($var.'text_in','',$n,10*$n);
  if ($query->param($var.'text_in')){
    $text = $query->param($var.'text_in');
    $text =~ s/\\//gi;
    $text =~ s/\$//gi;
    $text =~ s/\#//gi;
    $text =~ s/\~//gi;
    $text =~ s/\^/**/gi;
#		$v2 = abs(($v1-$ans)/$ans);
#print "<br>text = $text<br>";
#print "<br>v1 = $v1<br>";
#print "<br>ans = $ans<br>";
    if (($text eq $ans)||(lc($text) eq lc($ans))){
      print " <font color=green><EM>Right! </EM>.
      <IMG SRC=../icons/check.gif><font color=black>";
      &report("RIGHT ".$question."TEXT".$var."stud=",$text,$ans,$tempfile);
      return 1;
    }
    else{
      print " <font color=red><EM>Wrong! </EM>.
      <IMG SRC=../icons/checkno.gif><font color=black>";
      &report("WRONG "."TEXT".$var."stud=",$text,$ans,$tempfile);
      return 0;
    }
  }
}


txt_in

sub txt_in{
  my ($query) = $_[1];# this seems to work only in the newer version of CGI.pm
  my $var = $_[2];# this is changed in the calling routine, to provide a unique
#			name for variables in this subroutine, where they are concatenated
  my $ans = $_[3];# this the answer to be judged
  my $n = $_[4];#number of characters in input field, note that 10*this number = total length
  my $filename = $_[5];#student_file text
  my $debug = 'false';
  $tempfile = ">>../".$filename.".dat";
  print "<br><font color=red>You may enter an expression or a number here. `^' and `**' both mean exponentiation.";
  print " Be careful to observe proper bracketing.</font>";
  print "<br>",$query->textfield($var.'txt_in','',$n,10*$n);
  if ($query->param($var.'txt_in')){
    $txt = $query->param($var.'txt_in');
    $txt =~ s/\\//gi;
    $txt =~ s/\$//gi;
    $txt =~ s/\#//gi;
    $txt =~ s/\~//gi;
    $txt =~ s/\^/**/gi;
    $count = 0;
    for ($i=0;$i<length($txt);$i++){
#print "<br>i = ",$i," and count = ",$count, "<br> substr = ",substr($txt,$i,1);
      if(substr($txt,$i,1) eq '('){
        $count++;
      }
      elsif(substr($txt,$i,1) eq ')'){
        $count--;
        if($count < 0){print "<br><font color=red>Bracket error at position </font>",$i;}
      }
#print "done with loop one time";
    }
    
    if ($count != 0){print "<br><font color=red>Bracket error, more of one kind than another</font>";}
#   print "<br>txt = $txt";
    $v1 = eval($txt);
    $evalans= eval($ans);
    $v2 = abs(($v1-$evalans)/$evalans);
#    print "<br>txt = $txt<br>";
#    print "<br>v1 = $v1<br>";
#    print "<br>ans = $ans<br>";
    $crit = 0.001;
    if ($v2 < $crit){
      print " <font color=green><EM>Right! </EM>.
      <IMG SRC=../icons/check.gif><font color=black>";
      &report("RIGHT "."TXT :".$var,$evalans,$ans,$tempfile);
      return 1;
    }
    else{
      print " <font color=red><EM>appears to be incorrect. </EM>.
      <IMG SRC=../icons/checkno.gif><font color=black>";
      &report("WRONG "."TXT :".$var,$evalans,$ans,$tempfile);
      return 0;
    }
  }
}




round_up_to_exponent

sub round_up_to_exponent {
  local ($val, $exp) = @_;
  local ($tmp,$i,$tmp1,$tmp2);
#sprintf("\%.${exp}f", $val);
  $tmp = sprintf("\%.${exp}E", $val);
  if(index($tmp,"E+00") != -1){$tmp = $tmp =
    substr($tmp,0,length($tmp)-4);
  }
  $i = index($tmp,"E");
  if ($i != -1){
    $tmp1 = substr($tmp,0,length($tmp)-4);
    $tmp2 = substr($tmp,$i+1);
    return($tmp1."x10<sup>".$tmp2."</sup>");
  }
  
#	print "<br>tmp = $tmp";
  return $tmp;
}

Subroutine send_memo. Again, I suggest not using this kind of thing.

sub send_memo{
  my ($query) = $_[1];# this seems to work in the newer version of CGI.pm
  my $var = $_[2];# this is changed in the calling routine, to provide a unique
#	name for variables in this subroutine, where they are concatenated
  $student_name = $query->param('name');
  $sender = $query->param('remote_host');
  print $query->start_form;
  print "<hr>";
  print $query->hidden('faculty',$query->param('owner'));
  print $query->hidden('student',$query->param('name'));
#print $query->dump;
  $faculty_id = $query->param('owner');
  $student_id = $query->param('name');
  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
  if ($year le 50){$pre = "20"} else {$pre = "19"};
  print "<hr>";
print <<EOF;
If you wish to enter into a technical discussion (with me or others) and need
to use mathematical notation, consider using <a href=http://web.uconn.edu/~cdavid/cgi-bin/techwiki.pl>techwiki</a>
which allows technical/mathematical notation, but assumes that you separately
e-mail me (or others) saying that you've deposited something there which
you want me (or others) to read.
<br>If you just wish to write to me directly, without mathematical notation,
then please use the form below:
<hr>
EOF
#  print "<img src=../icons/mailbox.gif>";
  print "If you wish to comment to C. W. David about
  this question, assumptions you are making, inconsistencies in the
  phraseology of the question, objections to the question, etc., etc., etc.,
  you may use this space for that purpose.";
  print "<font color=red>";
  print "Please include your e-mail (in the form like mine, i.e., Carl.David\@uconn.edu) address explicitly. Thank you.";
  print "</font>";
  print "<br>e-mail addresss = ",$query->textfield($var.'eadd','',60,20);
#  print " @ $ENV{'REMOTE_HOST'}(";
#  print "$ENV{'REMOTE_ADDR'})\n";
  print $query->textarea($var.'comments','(If you want me to answer you,
you need to include your return address either here or above!)
e:mail=',10,80);
  print "<br>";
  print $query->submit($var.'Submit2','Send the comment');
  print $query->reset;
  print $query->end_form;
  if ($query->param($var.'Submit2') ) {
    open (FACULTY,">>../Master_teacher.dat")|| print
    "<br> Master_teacher.dat, Faculty File Troubles Encountered, please report to instructor";
    $comments = $query->param($var.'comments').localtime()."\n\nEND OF COMMENTS\n\n";
    $comments = $comments."-".$question;
    $eadd = $query->param($var.'eadd');
    $string = localtime().":".$comments.":from->".$eadd ;
    
    
    print FACULTY wrap("\t","","<",$query->script_name(),">,<",$student_id,">, comments=",$comments,"\n");
    close(FACULTY);
    
    $question = $query->script_name();
    $sender = $query->remote_host();
    $mailprog = '/usr/lib/sendmail';
    $recipient = 'david\@uconn.edu';
    open (MAIL, "|$mailprog $recipient") || die "Can't open $mailprog!\n";
    $string = "Subject: WWW comments (Forms submission)\n";
    $string = $string."Comments about the Freshman Chemistry server:\n";
    $string = $string."Student name: $student_id\n";
    $string = $string."Question: $question\n";
    $string = $string."e-mail account =  $eadd  \n";
    $string = $string."SENDER:  $sender  \n";
    $string = $string.":".localtime().":";
    $string = $string.$comments;
    print MAIL wrap("\t","",$string."\n");
    print MAIL  "\n------------------------------------------------------------\n";
    print MAIL "Server protocol: $ENV{'SERVER_PROTOCOL'}\n";
    print MAIL "Remote host: $ENV{'REMOTE_HOST'}\n";
    print MAIL "Remote IP address: $ENV{'REMOTE_ADDR'}\n";
    close (MAIL);
    
    print $query->param($var.'comments','');
# Make the person feel good for writing to us
    print STDOUT "<img src=../icons/postboxandhand.gif>";
    print STDOUT "Thank you for sending comments to C. W. David.";
    print STDOUT "<img src=../icons/emailed.gif>";
    
# ------------------------------------------------------------

blank_response

# subroutine blank_response
    sub blank_response
    {
      print "Your comments appear to be blank, and thus were not sent ";
      print "to our webmasters.  Please re-enter your comments, or ";
      print "Return to the <A HREF=\"/~cdavid/\">home page</A>, if you want.<P>";
      exit;
    }
  }
  
  
  
}#end of subroutine


#STOP end of send menu routines





true/false


sub tf{
# calling sequence, required!!!!!
  my ($query) = $_[1];# this seems to work only in the newer version of CGI.pm
  my $var = $_[2];# this is changed in the calling routine, to provide a unique
#			name for variables in this subroutine, where they are concatenated
  my $ans = $_[3];# this the either 'True' or 'False'
  my $filename = $_[4];
  $tempfile = ">>../".$filename.".dat";
  $question = $query->script_name();
  $vv1 = rindex($question,"\/");
  $len = length($question);
  $question = substr($question,$vv1+1,$len-$vv1).localtime();
  print $query->popup_menu( $var,['True','False'],'');
  if ($query->param($var)) {
    if ($query->param($var) eq $ans){
      print " <font color=green>was <EM>Right! </EM>.
      <IMG SRC=../icons/check.gif></font>";
      &report("RIGHT ".$question."(TF):".$var,"",$ans,$tempfile);
      return 1;
    }
    else {
      print " <font color=red>appears to be  <EM>Wrong! </EM>.
      <IMG SRC=../icons/checkno.gif></font>";
      &report("WRONG ".$question."(TF):".$var,"",$ans,$tempfile);
      return 0;
    }
  }
  
}


Subroutine save vars, which I suggest you drop, as did I.



sub save_vars{
shift;#takes out the reference of the module
$query = $_[0];
shift;
  my $var_string = $_[0];
  my $pointer_to_var_string = $_[1];
  $var_string = "'".$var_string."'";#make quoted
#  my	$a = $$pointer_to_var_string;#variable passed by pointer $_[1]
  if ($query->param($var_string) eq ''){
    $query->param($var_string,$$pointer_to_var_string);#if first
  }
  print $query->hidden($var_string,$$pointer_to_var_string);
  $$pointer_to_var_string = $query->param($var_string);
#	print "<br> in save, value = ",$query->param($var_string),"  for var = $var_string";
}

The original design, twenty+ years ago, had me keeping data on students as they worked the various problems. The idea was to use this data for improvement of the questions, as I learned about their errors.
Now-a-days, I think that most people would create a data base and record results, errors, comments, etc., in such a data base, so that TA's and lecturers could inspect the data base for information relative to their own students without having to log into the server, i.e., using some web page which allowed querys of the student data base.
I am too old to care about such things.