perl4q1a.pl, a standardized question

This is an explanation of the code which generates the last question (which can be seen here). This is a standard question in that it contains virtually all of the functionality which we will have to learn.
Later, we will be standardizing the method of asking questions so that certain common functions are carried out expeditiously. Specifically, we wish to monitor student activity, and give the student opportunity to complain. Therefore, we here include the original simple question re-cast into the form which most questions will ultimately present.
As you can see (below), the question is now much more complicated. You need only note that
  1. we are using a subroutine (CleanUp) to remove damaging characters from student input fields,
  2. that we are counting the number of times this page is accessed,
  3. that we are reporting student activity to a faculty file, and reporting wrong answers to a student error file (for review at a later time when error responses (help pages) may need adjustment),
  4. that we have added a comment line which allows students to tell the webmaster that something web-wise is wrong,
  5. and a memo line that allows students to complain about questions to their teacher.

The Master_teacher.dat file contains both the student's answers and the student's comments, the Master_student.dat file contains the student wrong answers. Both of these are in the public_html subdirectory. Here is an example of what is contained in the Master_teacher.dat file:
<AnyTeacher>=>q=/~cdavid/cgi-bin/book/home_stuff/proto_3.pl=student= AnyStudent entered a wrong answer this is a new wrong answer versus the corrrect answer = sqrt(4/x)13 hours, 11 minutes, Tuesday,4,14, 1996
<AnyTeacher>,<AnyStudent>, comments=this is a comment
The exact format of this output can be adjusted in reports.pl, in case this is excessively verbose. Notice that we have not bothered to keep the width of outputted lines to screen or printer widths, leaving that to a report generator program.
[ 1]#!/usr/local/bin/perl -- -*-perl-*-
[ 2]use CGI;
[ 3]$query = new CGI;
[ 4]print $query->header;
[ 5]print $query->start_html('Prototype Question_4-Logging&Commenting');
[ 6]require  "/home/cdavid/public_html/cgi-bin/CleanUp.pl";
[ 7]require  "/home/cdavid/public_html/cgi-bin/explain.pl";
[ 8]require  "/home/cdavid/public_html/cgi-bin/count.pl";
[ 9]require  "/home/cdavid/public_html/cgi-bin/reports.pl";
[10]srand;
[11]
[12]$scriptname = $query->script_name();#this gives the question name
[13]#print $query->dump;#use this to check that it worked! Just remove comment(#).
[14]
[15]print "Anonymous access to the <a href=/~cdavid/cgi-bin/test_send_memo.pl?var=$scriptname>webmaster to send a message concerning errors in this question</a>
[16]is available here.<br>";
[17]
[18]# this question was called via html?owner=Jones&name=SomeOne
[19]#$faculty_id = $query->param('faculty');
[20]#$student_id = $query->param('student');
[21]#print "<br>FACULTY(owner) = ",$query->param('owner'),", STUDENT(owner) = ",$query->param('name'),"<br>";
[22]
[23]print "<a href=http://chemphys.uconn.edu/~cdavid//notation/notation.html>Click here to get a notation explanation</a>";
[24]#print $query->dump;
[25]print "<hr>";
[26]&count($scriptname);
[27]print "<hr>";
[28]print $query->start_form;
[29]print $query->hidden('faculty',$query->param('owner'));
[30]print $query->hidden('student',$query->param('name'));
[31]#print $query->dump;
[32]$faculty_id = $query->param('faculty');
[33]$student_id = $query->param('student');
[34]print <<EOF;
[35]<P> How many feet are there in a mile?
[36]EOF
[37]print "<br>Answer = ",$query->textfield('ans','',50,80);
[38]print"<P> Query, is the above formula/number correct?",$query->submit;
[39]print $query->end_form;
[40]print "<hr>";
[41]print $query->start_form;
[42]print $query->hidden('faculty',$query->param('owner'));
[43]print $query->hidden('student',$query->param('name'));
[44]#print $query->dump;
[45]$faculty_id = $query->param('faculty');
[46]$student_id = $query->param('student');
[47]print "If you wish to comment to your <em>instructor</em> about
[48]this question, assumptions you are making, inconsistencies in the
[50]phraseology of the question, objections to the question, etc., etc., etc.,
[51]you may use this space for that purpose.";
[52]print "Nothing entered here will go to the webmaster, instead, it will
[53]go to your instructor!";
[54]print $query->textarea('comments','',10,60);
[55]print "<br>";
[56]print $query->submit('Submit2','Send the comment');
[57]#print $query->reset;removed 9/2020 as useless
[58]print $query->end_form;
[59]
[60]if ($query->param('comments') ne '') {
[61]	open (FACULTY,">>/home/cdavid/public_html/Master_teacher.dat")||die print 
[62]		"<br> Master_teacher.dat, Faculty File Troubles Encountered, please report to instructor";
[63]	$comments = $query->param('comments');
[64]	print FACULTY "<",$faculty_id,">,<",$student_id,">, comments=",$comments,"\n";
[65]	close(FACULTY);
[66]}
[67]
[68]if ( $query->param('ans') ne '')
[69]{
[70]$faculty_id = $query->param('faculty');
[71]$student_id = $query->param('student');
[72]#print $query->dump;
[73]#print "<br>student_id = ",$student_id;
[74]$stu_ans = $query->param('ans');
[75]$saved_stu_ans = $stu_ans;
[76]&CleanUp($stu_ans);#this removes potential hacker imbedded illegal commands
[77]$ans = 5280;
[78]
[79]
[80]
[81]
[82]print "student_answer = $saved_stu_ans";
[83]$stu_ans = eval($stu_ans);
[84]$ans =  eval($ans);
[85]#print "student_answer  = $stu_ans <p>";
[86]#print "student_answer evaluated = $stu_ans<p>";
[87]#print "answer evaluated = $ans <p>";
[88]($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
[89]if (eval (($stu_ans - $ans)**2 - 0.0001  ) ge 0.0 ) {
[90]	&reportstudent($faculty_id,$student_id,$saved_stu_ans,$saved_ans,$scriptname);	
[91]	&reportfaculty($faculty_id,$student_id,$saved_stu_ans,$saved_ans,$scriptname);	
[92]print ", <EM> Wrong</EM>,
[93]	<IMG SRC=http://chemphys.uconn.edu/Icons/checkno.gif\>"}
[94] else {print ", <EM> Right! </EM>,
[95]	<IMG SRC=http://chemphys.uconn.edu/Icons/check.gif\>" ;
[96]	&reportfaculty_right($faculty_id,$student_id,$saved_stu_ans,$saved_ans,$scriptname);	
[97]
[98]	}
[99]};
[100]
[101]print $query->end_html;

In the next section of this document, we will annotate the (above) code, line by line. We start with the precursor material required by Perl on the one hand, and CGI.pm on the other hand. All our questions start with this kind of material.
[ 1]#!/usr/local/bin/perl -- -*-perl-*-
[ 2]use CGI;
[ 3]$query = new CGI;
[ 4]print $query->header;
[ 5]print $query->start_html('Prototype Question_4-Logging&Commenting');
[ 6]require  "/home/cdavid/public_html/cgi-bin/CleanUp.pl";
Cleanup.pl is a program which we will show later which removes characters from student input guarding against hackers. Notice the addressing scheme ``/home/cdavid/public_html/cgi-bin/CleanUp.pl'', which is an absolute address. It would have been better to write ``./CleanUp.pl'', since CleanUp.pl is in the same cgi-bin directory as perl4q1a.pl, the question we are writing about. All of the following addresses are excessive, and might be made better through relative addressing.
[ 7]require  "/home/cdavid/public_html/cgi-bin/explain.pl";
explain.pl is a program which explains notations for mathematics in the context of the World Wide Web.
[ 8]require  "/home/cdavid/public_html/cgi-bin/count.pl";
count.pl is a counter program which tell us how many times this particular page has been called. You can read about it here,count.pl if you wish. This is a vary useful feature for helping you see what is popular and what isn't amongst the various parts of a site's offerings.
[ 9]require  "/home/cdavid/public_html/cgi-bin/reports.pl";
reports.pl is a program which writes reports (vide infra) to the disk about the student's activities.
[10]srand;
srand is a call to the random number generator to initialize. Calling this function at the beginning, if one operates with random numbers, guarantees that the random number generator will not repeat a sequence of numbers.
[11]
[12]$scriptname = $query->script_name();#this gives the question name
Statement 12 is a CGI.pm statement, recovering the name of this particular program. We use this in the report program, to write to the disk not only the name of the question (program) but other information (vide infra).
[13]#print $query->dump;#use this to check that it worked! Just remove comment(#).
Statement 13 is an unused debugging statement, erase the leading \# character, and it will print out the CGI.pm variables in use.
[14]
[15]print "Anonymous access to the <a href=/~cdavid/cgi-bin/test_send_memo.pl?var=$scriptname>webmaster to send a message concerning errors in this question</a>
[16]is available here.<br>";
We have written a program to send messages from the student to the teacher. This is home grown, because certain information needs to be sent by the student, and long experience indicates that such information is invariably missing. Hence the home grown mailer (vide infra). This program, test_send_memo.pl, not only gives the student a chance to comment on questions, or ask questions, but gives us a chance to discover what question the student is at when the memo was sent. Notice the call
test_send_memo.pl?var=$scriptname which passes the script's name which we obtained in Statement 12, to the mailer!
[17]
[18]# this question was called via html?owner=Jones&name=SomeOne
[19]#$faculty_id = $query->param('faculty');
[20]#$student_id = $query->param('student');
[21]#print "<br>FACULTY(owner) = ",$query->param('owner'),", STUDENT(owner) = ",$query->param('name'),"<br>";
[22]
These last lines of code were intended for multi-teacher multi-student record keeping. If the html call to this question has the teacher's and the student's name, then uncommenting Lines 19 and 20 make perl variables of these names, which we can use in our mailers, etc.. Statement 21 is a debug statement which is commented out.
[23]print "<a href=http://chemphys.uconn.edu/~cdavid//notation/notation.html>Click here to get a notation explanation</a>";
Line 23 is a link to an html page which contains material about notation which might be of interest to the student.
[24]#print $query->dump;
[25]print "<hr>";
Statement 24 is an unused debug statement. Statement 25 shows how, in perl, we send html code to the browser window. In this case, we are sending the statement to place a rule across the window, i.e., a separator.
[26]&count($scriptname);
Here is where we use the counter program: count.pl. Explanation of this program will be made later.
Continuing
[27]print "<hr>";
[28]print $query->start_form;
Here we start the question itself, i.e., we start the form which will be processing the student's response. First, we print out (in hidden form) the owner and name parameters passed to the question in its call. It is important to realize that the WWW passes information to a called URL by means of an inquiry string appended to the address of the URL itself. This a call to http://chemphys.uconn.edu/~cdavid/cgi-bin/program.pl call a program, but a call to http://chemphys.uconn.edu/~cdavid/cgi-bin/program.pl?owner=DAVID does more, passing a parameter, owner, with the value DAVID. When the called program.pl starts running, it has owner=DAVID as a parameter, which can be retrieved. We actually did this in lines [19] and [20] (above), and assigned owner's value to $faculty_id, etc.. So, here we save these parameters, using the ``hidden'' trick, and restore them [32]-[33] just in case.
[29]print $query->hidden('faculty',$query->param('owner'));
[30]print $query->hidden('student',$query->param('name'));
[31]#print $query->dump;
[32]$faculty_id = $query->param('faculty');
[33]$student_id = $query->param('student');
Here we make sure that the teacher's and student's names are preserved between invocations of the script.
[34]print <<EOF;
This is a handy scheme for writing text to stdout. This Statement [34] says, write the following lines to stdout until the EOF symbol shows. There could be more lines of text in the question. How many feet are there ... is kind of a minimal question, but any legal HTML and text, is OK, as many lines as you wish!
[35]<P> How many feet are there in a mile?
[36]EOF
Here, at Statement [36], we see the EOF symbol which is the terminator of the previous print Statement [34].
[37]print "<br>Answer = ",$query->textfield('ans','',50,80);
At statement [37] we put up a textfield for the student, into which she is supposed to enter her answer.
[38]print"<P> Query, is the above formula/number correct?",$query->submit;
Statement [38] puts a submit button on the screen.
[39]print $query->end_form;
[40]print "<hr>";
These last two Statements, [39] and [40] end the form and draws a separator line.
Next, we start a new form, in which the student is given the opportunity to comment. Clearly, students can question the question, i.e., claim there is something wrong, or missing, or whatever, or they can complain, or vent their spleens, etc., etc., etc.. In any case, we need to offer them a chance to do this, and the first method we employ is to allow them to write comments into a file.
[41]print $query->start_form;
[42]print $query->hidden('faculty',$query->param('owner'));
[43]print $query->hidden('student',$query->param('name'));
[44]#print $query->dump;
[45]$faculty_id = $query->param('faculty');
[46]$student_id = $query->param('student');
After starting the form, we hide the owner and name CGI variables (vide infra) and then restore them, using Statements [44] and [45]. Now $faculty_id and $student_id have values, which were passed when the script was invoked (vide infra).
[47]print "If you wish to comment to your <em>instructor</em> about
[48]this question, assumptions you are making, inconsistencies in the
[50]phraseology of the question, objections to the question, etc., etc., etc.,
[51]you may use this space for that purpose.";
[52]print "Nothing entered here will go to the webmaster, instead, it will
[53]go to your instructor!";
Inside the form, we explain to the student that this message area is to be used to offer comments on the nature of the question. Notice that we can write anything we like to the student here.
[54]print $query->textarea('comments','',10,60);
Statement [54] is the one which puts up a box 10 lines long, and 60 characters wide, into which students can offer their comments. One small point, it is a pain in the neck to have this kind of text box, since what happens is that the student types this enormous string, which may or may not contain linefeeds and carriage returns, making it awfully hard to read. We will address this later.
[55]print "<br>";
[56]print $query->submit('Submit2','Send the comment');
[57]#print $query->reset;removed 9/2020 as useless
[58]print $query->end_form;
We put another submit button up on the screen at Statement [56], and here we add another button, a reset button, which, when activated, puts the cursor at the top left corner of the box (the reset position). Finally, we end the form.
[59]
[60]if ($query->param('comments') ne '') {
Statement [60] starts an if-block, based on whether or not there are comments in the comment field, i.e., the comments are not blank. The statement
$query->param('comments')
retrieves a CGI.pm variable, which was defined in Statement [54]. If the student did not fill in anything, this retrieved value is '', i.e., blank, and since Statement [50] is asking it the comment is not equal to '' (blank), it follows that the if-block will not be executed unless the student has put something into the textarea!
[61]	open (FACULTY,">>/home/cdavid/public_html/Master_teacher.dat")||die print 
[62]		"<br> Master_teacher.dat, Faculty File Troubles Encountered, please report to instructor";
Statement [61] opens a file, whose Perl name (handle) will be FACULTY. The file is being opened in append mode, i.e., '>>'. In this mode, what the student wrote will be added to the end of the existing file, i.e., appended. A single greater than sign would have resulted in erasing the old file and putting the student's comments into the file (as its only contents). The file's name is installation dependent, and you should not use the file designation that I used to define where your file will be. What is important is that the file be write enabled by the browser, which may be known to the Unix system as nobody, or guest, or whatever (this depends on how the server is configured). Trial and error are the words here.
[63]	$comments = $query->param('comments');
Statement [63] assigns the CGI.pm variable 'comments' to the Perl variable $comments.
[64]	print FACULTY "<",$faculty_id,">,<",$student_id,">, comments=",$comments,"\n";
[65]	close(FACULTY);
'print' writes out the comment to the FACULTY file. It uses the handle which we defined in Statement [61]. The actual line being printed contains the faculty_id, the student_id, both of which were passed to this script (Perl program), and the comments themselves. Once the print is done, we close the file, so that it is free to be used by others.
[66]}
[67]
[68]if ( $query->param('ans') ne '')
[69]{
Statement [68] begins an if-block, which will be executed if and only if the student has placed something into the textfield defined in Statement [37]. 'ans' is a CGI.pm variable, and must be retrieved.
[70]$faculty_id = $query->param('faculty');
[71]$student_id = $query->param('student');
[72]#print $query->dump;
[73]#print "<br>student_id = ",$student_id;
Statement [70] retrieves the value of the faculty_id, while [71] retrieves the student's i.d., so that we can use this information later. Statements [72] and [73] are commented out debugging statements which help us insure that we have correct values of things.
[74]$stu_ans = $query->param('ans');
[75]$saved_stu_ans = $stu_ans;
We obtain the student's answer in Statement [74], where we obtain the CGI.pm variable 'ans' which was defined in Statement [37]. The $query->param call always assigns a CGI.pm variable to a Perl variable if it is so coded (i.e., $var_ans = $query->param('ans') assigns the CGI.pm variable 'ans' to the Perl variable $var_ans.)
[76]&CleanUp($stu_ans);#this removes potential hacker imbedded illegal commands
CleanUp is a home written subroutine which is in the cgi-bin directory (see require statement, Statement [6]. Here is the text for CleanUp.pl:
sub CleanUp
{
my($v,$count,$i);
$v = $_[0];
if ( $_[0] ne '')
{
	$_[0] =~ s/\\//gi; 
	$_[0] =~ s/\\$//gi; 
	$_[0] =~ s/\\#//gi; 
	$_[0] =~ s/\~//gi;
	$_[0] =~ s/\^/**/gi;
$count = 0;
for ($i=0;$i<length($v);$i++){
#print "<br>i = ",$i," and count = ",$count, "<br> substr = ",substr($v,$i,1);
if(substr($v,$i,1) eq '('){ 
	$count++;
	}
elsif(substr($v,$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>";}
}
}
return -1;
We will discuss this later; you can read about CleanUp here. Suffice it to say that CleanUp does two things, it first removes hacker symbols from a student's answer which might be used to break into our computer, and secondarily, it counts brackets and tells the students if the number of left brackets does not equal the number of right brackets.
[77]$ans = 5280;
[78]
Here we define the correct answer, 5,280. Of course we do not use the comma, since this is a Perl value. Remember, it is crucial to have correct answers, since students trust the computer too much, and get really angry when it is wrong.
We save the student's answer in $saved_ans so that we can print out on the browser's window what the computer thought the student entered.
[82]print "student_answer = $saved_stu_ans";
Here, at Statement [82] we report the answer that the student gave. We saved her answer at line [75] (above). We do this because some students have trouble entering materials into these windows, and therefore need to look at what the computer thinks they entered.
[83]$stu_ans = eval($stu_ans);
[84]$ans =  eval($ans);
This is the key! Here we evaluate the student's answer and our own, and decide if the student is correct. You can see why it is important that our answer be correct!
Anyway, the advantage of using 'eval' is that it allows a student to write
2*5280/2
if she wishes to, and have it correctly intepreted. 'eval' evaluates the expression, and returns a numerical value! If the student wrote 2640 + 2640, eval would evaluate the expression to 5280 and, in the next few lines, accept the answer as correct. This scheme lies at the heart of all CAT/H work.
[85]#print "student_answer  = $stu_ans <p>";
[86]#print "student_answer evaluated = $stu_ans<p>";
[87]#print "answer evaluated = $ans <p>";
These are debugging statements which are here commented out.
[88]($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
For future reference, we get the time and date that this answer was submitted here. The call assigns the output of localtime to the various Perl variables on the left hand side of the assignment statement. We may print them in the log when the time comes.
[89]if (eval (($stu_ans - $ans)**2 - 0.0001  ) ge 0.0 ) {
We now compare the difference between the student's answer and our answer, evaluated, and to prevent obvious silliness, we compare either the absolute value of the difference, or in this case, the square of the difference, in either case demanding that the comparison be made on a positive number! 0.0001 is an arbitrary threshold. If the student answerered 5280.001, i.e., was fooling arround, we could declare her wrong, provided that we set the threshold correctly. If the student answer is significantly different than our answer, we execute the following clause of the if statement:
[90]	&reportstudent($faculty_id,$student_id,$saved_stu_ans,$saved_ans,$scriptname);	
[91]	&reportfaculty($faculty_id,$student_id,$saved_stu_ans,$saved_ans,$scriptname);	
[92]print ", <EM> Wrong</EM>,
[93]	<IMG SRC=http://chemphys.uconn.edu/Icons/checkno.gif\>"}
reportstudent is a subroutine (to be discussed later) which writes the teacher id, the student id, the student's answer, the correct answer, and the question name to a file. Also, we print a big red X .
[94] else {print ", <EM> Right! </EM>,
[95]	<IMG SRC=http://chemphys.uconn.edu/Icons/check.gif\>" ;
[96]	&reportfaculty_right($faculty_id,$student_id,$saved_stu_ans,$saved_ans,$scriptname);	
[97]
[98]	}
If the student is right, these statements are executed. A check mark (green, in our case) shows.
[99]};
[100]
[101]print $query->end_html;
Now, we have to go back and fill in some details. First, there's CleanUp. You can read about CleanUp here.
Here is where I've stopped editingSorry.