Приглашаем посетить
Горький (gorkiy-lit.ru)

15.4 Practical CGI applications with Perl

Table of Contents

Previous Next

15.4 Practical CGI applications with Perl

15.4.1 Online examinations and marking

Online examinations or testing is not a feature exclusively reserved by schools or colleges. Many marketing research firms and agencies also use this feature to get results for their clients. The online examination page that we are going to develop has the following features:

  • Flexible and easy to add or change questions and answers.

  • Returns results to user immediately.

  • Leaves a permanent record of all results on the server.

  • Provides a simple statistical file on the server for the analysis of the results.

To simplify the implementation, all questions are of multiple-choice type. The interface part is listed below:



Example: ex15-12.htm - Online Exam. And Marking

 1: <?xml version="1.0" encoding="iso-8859-1"?>
 2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 3:    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 4: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 5: <head><title> Online Exam. And Marking  ex15-12.htm</title></head>
 6: <style>
 7:  .tx01{font-size:14pt;color:#000088;font-weight:bold}
 8:  .tx02{font-family:arial;font-size:14pt;background:#ffffff;color:#000000}
 9:  .butSt{background-color:#aaffaa;font-family:arial;font-weight:bold;
10:     font-size:14pt;color:#008800;width:30px;height:30px}
11: </style>
12: <body style="background:#bbbbff;font-family:arial;color:#000088">
13: <script>
14:  var noq = 3
15:  var qq = new Array(
16:     "Which one of the following is the capital of China?",
17:        "London", "New York", "Beijing", "Hong Kong",
18:     "Which of the following is NOT a country?",
19:        "Kenya","India","Singapore","Seoul",
20:     "Which of the following is NOT a river?",
21:        "Thames","Yellow","Nile","Coco")
22: </script>
23:
24: <div style="font-size:18pt;font-weight:bold;position:absolute;
25:    top:20px;left:170px">
26:    Please Answer All Questions Below<br />ABC School
27:  <form method="post" action="ex15-12.pl">
28:   <table class="tx01" align="center">
29:    <tr><td>Name:</td>
30:     <td><input class="tx02" type="text" name="from" id="from"></td>
31:     <td>&nbsp&nbspId. Number:</td>
32:     <td><input class="tx02" type="text" name="noId" id="noId"></td></tr>
33:   </table>
34:   <img src="line1.gif" height="6" width="600" />
35:   <div id="showq"></div>
36:  </form>
37: </div>
38: <script src="ex15-12.js"></script>
39: </body>
40: </html>

One of the basic methods to generate flexible online questions is to use ECMAScript. This way, the XHTML document would be quite simple and easy to read. In this page, a variable noq (i.e., number of questions) is used to represent how many questions we want to generate. Soon after that, an array is declared to store the questions and answers. To simplify the coding, the questions are arranged in a question/answers format. Only four possible choices of answer are allowed. That is, we start the array with the question and all four possible answers are attached right after it. From lines 1621, you can clearly see that we have three questions and each question has four choices of answer. This page also contains some input fields to obtain the user name and identity number of the candidate. The generated questions are displayed in line 35. A screen shot of this page is shown in Fig. 15.16.

Figure 15.16. ex15-12.htm

graphics/15fig16.jpg


From this figure you can see that the three questions are arranged into three big rows. Each question has four answers right under it. The four answers are formatted into two rows using radio boxes. To generate this format, the following file ex15-12.js is used:



Example: ex15-12.js - The ECMAScript Program File For ex1512.htm

 1: lst='<table class="tx01" style="position:absolute;'+
 2:   'top:160px;left:10px;width:550px">'
 3:
 4:   for (ii=0;ii<noq;ii++)
 5:   {
 6:    lst = lst + '<tr><td colspan="4"><br />('+(ii+1)+')&nbsp;'+
 7:          qq[ii*5]+'</td></tr>'
 8:
 9:    for (kk=0;kk<2;kk++)
10:    {
11:     lst = lst +'<tr align="center">'
12:     for (jj=1;jj<3;jj++)
13:     {
14:       lst = lst + '<td>'+qq[ii*5+kk*2+jj]+'</td><td>'+
15:       '<input class="butSt" type="radio" name="q'+(ii+1)+
16:       '" id="q'+(ii+1)+'" value="'+ qq[ii*5+kk*2+jj]+'" /></td>'
17:     }
18:     lst = lst +"</tr>"
19:    }
20:   }
21: lst = lst +'<tr><td colspan="4" style="text-align:center"><br /><br />'+
22:    '<input class = "butSt" style="width:200px" type="submit" '+
23:         'value="Send"> &nbsp&nbsp '+
24:    '<input class = "butSt" style="width:200px" type="reset" '+
25:         'value="Reset"></td></tr>'
26:   lst = lst +'</table>'
27:
28:   document.getElementById("showq").innerHTML=lst

To formulate the question/answers format, a string containing an XHTML table is constructed in this ECMAScript file. After the table declaration in line 1, a triple for-loop is employed in lines 420 to generate the table rows and cells. If you set ii=0 and substitute it into the for-loops, this will produce



<tr><td colspan="4"><br /> (1) qq[0]</td></tr>
<tr align="center">
  <td>qq[1]</td><td><input class="butSt"
    type="radio" name="q1" id="q1" value="qq[1]" /></td>
  <td>qq[2]</td><td><input class="butSt"
    type="radio" name="q1" id="q1" value="qq[2]" /></td>
</tr>
<tr align="center">
  <td>qq[3]</td><td><input class="butSt"
    type="radio" name="q1" id="q1" value="qq[3]" /></td>
  <td>qq[4]</td><td><input class="butSt"
    type="radio" name="q1" id="q1" value="qq[4]" /></td>
</tr>

Since the array elements q[0], q[1], …, q[4] store the question and the four answers for question 1, we have formatted question 1 into these table rows. Note that all radio boxes have name="q1" to represent the question identity. After the Send and Reset buttons, the table is constructed and displayed by the statement in line 28.

Once the questions are answered, the entire form is submitted to the Perl script ex15-12.pl for processing. The functions of the Perl script are as follows:

  • Get the selected answer for each question.

  • Compare to the correct ones and mark the examination.

  • Return result to the candidate immediately.

  • Open a file to store all results.

  • Open another file to perform some simple statistical analyses.

The first part of the script file ex15-12.pl is listed below:



Example: ex15-12.pl - The Perl Script For ex1512.htm (Part One)

 1: #! /usr/bin/perl
 2: use CGI qw( :standard );
 3:
 4: my $totQ = 3;               ## Total number of questions
 5: my $totM = 0;               ## Total marks
 6: my $timeV = scalar(localtime());
 7:
 8: @Ans = ("Beijing","Seoul","Coco");
 9: my $from = param( "from" );
10: my $noId = param( "noId" );
11:
12: for ($ii=0;$ii<$totQ;$ii++)
13: {
14:   $result[$ii]="Wrong";
15:   my $tmp = 'q'. ($ii+1);
16:   $qi[$ii] = param($tmp);   ## Get the answer from users
17:   $qStat[$ii]=0;            ## Set all questions answered incorrectly
18: }
19:
20: for ($ii=0; $ii< $totQ; $ii++)
21: {
22:  if ($qi[$ii] eq $Ans[$ii])
23:  {
24:     $result[$ii] = "Correct";
25:     $totM = $totM +1;
26:     $qStat[$ii]=1;
27:  }
28: }
29:

First, we have some Perl variables to store the total number of questions and marks. After that, the correct answers of all questions are declared inside an array named @Ans. To get the user's submitted answers, a for-loop is used in lines 1218. For example, if you substitute $ii=0 into the for-loop, you will have the pseudo code



$result[0]="Wrong";
$tmp = 'q1';
$qi[0]=param($tmp);
$qStat[0]=0;

The first line is to create a variable to assume that the user has selected the wrong answer. The second and third lines together would get the user's selection on question 1 and store the value into variable $qi[0]. The final variable in the pseudo code $qStat[0] is used to remember how many candidates answered this question correctly.

The marking process is performed by another for-loop in lines 2028. Again, if you substitute $ii=0 into the for-loop, you will have the pseudo code



if ($qi[0] eq $Ans[0]) {
     $result[0] = "Correct";
     $totM = $totM +1;
     $qStat[0]=1;
}

This is a simple if statement. That is, if the user answers the question correctly, the value of $result[0] is set to "Correct"; increment the total marks of this candidate $totM by 1 and set the variable $qStat[0] to indicate that this candidate answered this question correctly.

The second part of the script is to return the results to the candidate immediately. This is just a simple task to return an XHTML document to the candidate.



Listing: Continuation Of The Perl Script ex15-12.pl (Part Two)

30: print "Content-type:text/html\n\n";
31:
32: print << "mypage";
33:  <?xml version="1.0" encoding="iso-8859-1"?>
34:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
35:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
36:  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
37:  <head><title>Example  ex15-12.pl </title></head>
38:  <body style="background:#000088">
39:  <div style="font-family:arial;font-size:18pt;color:#ffff00">
40:    Name : $from <br />
41:    Id. Number: $noId <br />
42:    Your Test Results are as follows:<br /><br />
43:   <img src="line1.gif" height="6" width="400" /><br /><br/>
44: mypage
45:
46:   for ($ii=1; $ii <= $totQ; $ii++)
47:   {
48:     print "Question $ii = $qi[$ii-1] ($result[$ii-1]) <br />";
49:   }
50:   print "Total **** $totM out of $totQ";
51:
52: print << "endpage";
53:   <br/><br />
54:   <img src="line1.gif" height="6" width="400" /><br />
55:   Date: $timeV<br />
56:  </div></body></html>
57: endpage
58:

This XHTML page is quite easy to understand. After the name and identity number, the results of the examination are returned inside a for-loop in lines 4650. Since the elements for arrays $qi[] and $result[] start from zero, an adjustment to variable $ii1 inside the print statement in line 48 is used. Together with the result for each question, if the candidate answers two questions correctly, a message



Total **** 2 out of 3

is displayed to represent the total marks of the examination. A screen shot of this page in action is shown in Fig. 15.17.

Figure 15.17. Returned exam. result

graphics/15fig17.jpg


The third part of the program script opens two files. The first file is to store all the results and the second file is to store how many candidates answer each question correctly.



Listing: Continuation Of The Perl Script ex15-12.pl (Part Three)

59: open(outfile,">>marking.dat")
60:   or die("Cannot Open File: marking.dat");
61:   print outfile " name = $from \n Id. Number = $noId\n Date = $timeV\n\n";
62:   for ($ii=1; $ii <= $totQ; $ii++)
63:   {
64:     print outfile " Question $ii = $qi[$ii-1] ($result[$ii-1]) \n";
65:   }
66:   print outfile " Total **** $totM out of $totQ \n";
67:   print outfile "================================================\n";
68: close(outfile);
69:
70: open(statfile,"+<markstat.dat")
71:   or die("Cannot Open File: markstat.dat");
72:
73:   for ($ii=0; $ii <= $totQ; $ii++)
74:   {
75:     $getData[$ii] = <statfile>;
76:   }
77:   seek(statfile,0,0);
78:
79:   $getData[0]++;
80:   print statfile "$getData[0]\n";
81:   for ($ii=1; $ii <= $totQ; $ii++)
82:   {
83:    $getData[$ii] = $getData[$ii] + $qStat[$ii-1];
84:    print statfile "$getData[$ii]\n";
85:   }
86: close(statfile);
87:

Lines 5968 open a file called marking.dat. This file stores just about everything that is returned to the candidate. Since we have used an append mode for the file opening, the records for each examination candidate are appended together as in Fig. 15.18.

Figure 15.18. ex15-12.htm

graphics/15fig18.gif


In order to store some statistical results such as how many candidates answer each question correctly, another file called statmark.dat is used. This file has a column of zeros when started. The first line of the file represents how many candidates took part in the examination. The second line represents how many candidates answered question 1 correctly. For subsequent lines, a similar representation is used.

For the statistics file, a for-loop in lines 7376 is used to read the data into the array $getData[]. Then you need to rewind the file in line 77 so that you can write something into the file again. After incrementing the total number of participants by 1, this value is written back to the file. Then for each question, the value of how many candidates answered it correctly is updated in line 83 by



$getData[$ii] = $getData[$ii] + $qStat[$ii-1];

These values are written back to the file markstat.dat. For example, after three candidates finished the examination as shown in Fig. 15.18, the statistics file has the values:



3  Total participants
1  One candidate answered question1 correctly
2  Two candidates answered question2 correctly
1  One candidate answered question3 correctly

Online examinations and marking are good practice for interactive Web programming with CGI. For a more practical and interactive demonstration, we consider an online seat reservation page.

15.4.2 A page to reserve your seats online

A more interactive example on the Web may be a utility to book or reserve your seats online. This kind of utility is widely used in many seat reservations for trains, theaters, and planes. It is obvious that a full commercial implementation of a seat reservation page is beyond the scope of this book. In this section, however, a cut-down version is developed.

The first thing to do is to design the data file to represent all the seats and reservations. For simplicity, we only have 12 seats and the data file is a text-based one called seat.txt. The first line of the file contains a number to represent how many seats are in the page. The data structure of the seat file looks like that given in Table 15.2.

Table 15.2. Data structure of the seat file

1:

12

< --- Number of seats

2:

0, (Empty),Tel:

< --- First seat is empty

3:

1, (Mr. Brown), Tel: 01890-12344321

< --- This seat is booked

x:

… … …

 

x:

… … …

 

12:

0, (Empty), Tel:

< --- Eleventh seat is empty

13:

0, (Empty), Tel:

< --- Twelfth seat is empty


In line 2, the first 0 value indicates that the seat is not yet reserved (reservation flag). Therefore, the name field next to the zero is (Empty) and the telephone field has no number. The third line demonstrates a typical reserved seat by Mr. Brown with his name and telephone number.

The basic seat reservation page that we are going to develop has two parts. The first part is a user interface to perform the following tasks:

  • Read the data file and display the data as seats with numbers.

  • If a seat has not been reserved by someone, the seat is represented by a checkbox.

  • If a seat has been reserved, the name of the person who reserved it is displayed.

  • Users can freely check the checkboxes to reserve the seat they want.

To read the data file, the following Perl script is used:



Example: ex15-13.pl - Reserve Your Seats Online (Part One)

 1: #! /usr/bin/perl
 2: use CGI qw( :standard );
 3: print "Content-type:text/html\n\n";
 4:
 5: my $timeV = scalar(localtime());
 6: my $noSeat=0;
 7:
 8: open(filehandle,"<seat.dir/seat.txt")
 9:    or die("Cannot Open seat.txt File.. Error");
10: my $noSeat = <filehandle>;
11: for ($ii=1;$ii <= $noSeat;$ii++)
12: {
13:   $tmp01 = <filehandle>;
14:   ($seatNote[$ii],$seatName[$ii]) = split(',',$tmp01);
15: }
16: close(filehandle);
17:

After opening the data file seat.txt, the first line is read into the variable $noseat in line 10. This value represents the total number of seats in the page. A simple for-loop is employed to read in all lines of the data file. For each line, the seat reservation flag and the name of the person who booked the seat are extracted into array variables $seatNote[] and $seatName[].

When information on the seats is available, you can construct an XHTML page to display it on the browser screen. The Perl script to generate the XHTML page is



Listing: Continuation Of The Perl Script ex15-13.pl (Part Two)

18: print << "mypage";
19:  <?xml version="1.0" encoding="iso-8859-1"?>
20:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
21:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
22:  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
23:  <head><title></title></head>
24:  <style>
25:    .butSt{background:#aaffaa;font-family:arial;font-weight:bold;
26:      font-size:14pt;color:#008800;width:200px;height:30px}
27:    .chkSt{background-color:#aaffaa;font-family:arial;font-weight:bold;
28:      font-size:14pt;color:#008800;width:30px;height:30px}
29:    .txtSt{font-weight:bold;font-size:14pt;color:#ffff00}
30:  </style>
31:  <body style="background:#000088;font-family:arial">
32:  <div class="txtSt" style="text-align:center;font-size:18pt">
33:     Reserve Your Seat Here
34:   <form action="ex15-13a.pl" method="post">
35:   <table style="width:550px" border="0" class="txtSt" align="center">
36:    <tr><td>Name: </td>
37:      <td><input type="text" id="from" name="from" class="butSt" /></td>
38:      <td>Tel: </td><td>
39:      <input type="text" id="tel" name="tel" class="butSt"></td></tr>
40:   </table><br />
41:   <img src="line1.gif" height="6" width="600" /><br /><br/>
42:   <table class="txtSt" cellspacing="30" align="center"
43:         style="width:650px;text-align:center">
44: mypage
45: $noCols = 3;
46: for($jj=0;$jj<$noCols;$jj++)
47: {
48:  print"<tr>";
49:  for ($ii=1;$ii<5;$ii++)
50:  {
51:    $tmp = $jj*4 + $ii;
52:    $tmpSt = 'ch' . $tmp;
53:    if ($seatNote[$tmp] == 0)
54:    {
55:      print "<td>Seat $tmp <br />$seatName[$tmp]<br />
56:         <input type=\"checkbox\" class=\"chkSt\" name=\"$tmpSt\"
57:         id=\"$tmpSt\" value=\"0\" /></td>";
58:    } else {
59:      print "<td style=\"color:#ffffff\">Seat $tmp <br />
60:        $seatName[$tmp]<br /><input type=\"checkbox\"
61:        style=\"visibility:hidden\" class=\"chkSt\" name=\"$tmpSt\"
62:        id=\"$tmpSt\" checked value=\"1\" /></td>";
63:    }
64:  }
65:  print "</tr>";
66: }
67:
68: print << "endpage";
69:   </table><br />
70:   <table align="center"><tr><td>
71:   <input type="submit" class="butSt" value="Submit" /> &nbsp;&nbsp;
72:   <input type="reset" class="butSt" value="Reset" /></td></tr></table>
73:   </form>
74:   <img src="line1.gif" height="6" width="600" /><br />
75:   Date: $timeV<br />
76:  </div></body></html>
77: endpage

This script fragment generates an XHTML page with one form and three tables inside it. The first table defined in lines 3540 is designed to obtain the user's information. That is, the name and telephone number of the person who wants to reserve a seat. The second table defined in lines 4269 is more interesting. The table rows and cells are generated by a double for-loop. Since we want to arrange the seats into four columns, three rows are needed. That is, the outer for-loop is responsible for generating three rows and the inner for-loop is responsible for generating four columns of seats.

For each seat, if the seat reservation note $seatNote[] equals 0 (line 53), a checkbox is generated in lines 5557. If $seatNote[] is not 0, the name of the person who reserved it is displayed. A checkbox with visibility set to hidden is also generated so that the checkbox is not displayed. A screen shot of this script is shown in Fig. 15.19.

Figure 15.19. ex15-13.p1

graphics/15fig19.jpg


From Fig. 15.19, you can see that the reserved seats are displayed with names and no checkbox is attached.

At the end of the script, a table containing Submit and Reset buttons is defined. When the Submit button is pressed, another Perl script ex15-13a.pl is activated to process the data and perform the seat reservation on the server.

The user him- or herself or some operators can fill in the form by

  • entering the name and telephone number of the person who wants to reserve a seat;

  • selecting some seats to reserve, such as seat 11 and seat 12.

Once the form is submitted and accepted, an acknowledgment page (Fig. 15.20) is returned by the system to indicate that the selected seats are reserved. If the user presses the Back button at this time, the page is updated with new reservations as in Fig. 15.21.

Figure 15.20. A return page

graphics/15fig20.jpg


Figure 15.21. ex15-13.p1

graphics/15fig21.jpg


The data file seat.txt of the seats is also updated as in Fig. 15.22.

Figure 15.22. Data file seat.txt

graphics/15fig22.gif


To see the techniques behind the display, let's consider the Perl script ex15-13a.pl. The first part of this script is responsible for reading the seat data file and collecting data including the selected seats from the user page.



Example: ex15-13a.pl - The Perl Script For ex1513.pl (Part One)

 1: #! /usr/bin/perl
 2: use CGI qw( :standard );
 3: print "Content-type:text/html\n\n";
 4:
 5: my $timeV = scalar(localtime());
 6: my $noSeat=0;
 7:
 8: open(filehandle,"<seat.dir/seat.txt")
 9:    or die("Cannot Open seat.txt File.. Error");
10: my $noSeat = <filehandle>;
11: for ($ii=1;$ii <= $noSeat;$ii++)
12: {
13:   my $tmp01 = <filehandle>;
14:   $tmp01 =~ s/\n//g;
15:   ($seatNote[$ii],$seatName[$ii],$telephone[$ii]) = split(',',$tmp01);
16: }
17: close(filehandle);
18:
19: for ($ii=1;$ii<= $noSeat;$ii++)
20: {
21:   my $tmp = 'ch'. ($ii);
22:   $checkB[$ii] = param($tmp);
23: }
24: $from = param("from");
25: $tel = param("tel");
26:

After opening the data file seat.txt, a for-loop in lines 1116 is used to read in all the seat data. For each data line about the seat, a substitution is performed in line 14 to chop off the line break. Then the line is split into $seatNote, $seatName, and $telephone.

To obtain the selected checkboxes from the interface, a for-loop is employed in lines 1923 to check all the checkboxes. If a checkbox is checked by the user, the value of the array element $checkB[] contains the value 0. The user name and telephone number are also obtained in lines 2425. Once all this information is available, you can construct a return page back to the user. The construction is demonstrated in the following script fragment:



Listing: Continuation Of The Perl Script ex1513a.pl (Part Two)

27: print << "mypage";
28:  <?xml version="1.0" encoding="iso-8859-1"?>
29:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
30:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
31:  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
32:  <head><title></title></head>
33:  <style>
34:    .butSt{background-color:#aaffaa;font-family:arial;font-weight:bold;
35:      font-size:14pt;color:#008800;width:130px;height:30px}
36:    .txtSt{font-weight:bold;font-size:14pt;color:#ffff00}
37:  </style>
38:  <body style="background:#000088;font-family:arial">
39:  <div style="font-size:18pt;color:#ffff00;text-align:center">
40:    The Following Seats Have Been <br /> Reserved For<br /><br />
41:  <img src="line1.gif" height="6" width="400" /><br /><br/>
42:  <form action="ex15-13.pl" method="post">
43:  <table class="txtSt" align="center">
44:   <tr><td>Name : </td><td>$from</td></tr>
45:   <tr><td>Tel: </td><td>$tel </td></tr>
46: mypage
47:
48:  for ($ii=1; $ii <= $noSeat; $ii++)
49:  {
50:   if (($checkB[$ii] ne '') && ($checkB[$ii] !=1))
51:   {
52:    print "<tr><td>Seat Number $ii = </td><td>(Reserved)</td></tr>";
53:    $seatNote[$ii] = 1;
54:    $seatName[$ii] = "($from)";
55:    $telephone[$ii] = "Tel: $tel";
56:   }
57:  }
58:  print "</table><br /><br />";
59:
60: print << "endpage";
61:   <input type="submit" class="butSt" value="Back" />
62:   </form>
63:   <img src="line1.gif" height="6" width="400" /><br />
64:   Date: $timeV<br />
65:  </div></body></html>
66: endpage
67:

All the checkboxes are represented by the array $checkB[]. Since the system only returns those checkboxes which are checked, a simple testing criterion in line 50



($checkB[$ii] ne '') ## not empty checkbox

can isolate all checked checkboxes. All the previous boxes checked or seats reserved by users will have the value $checkB[]=1. Therefore, if you add the condition as in the second half of line 50



$checkB[$ii] !=1

you can isolate the checkboxes that the current user has checked. These checkboxes are displayed and returned back to the current user by the print statement in line 52. Also, inside this for-loop, the following data are prepared (lines 5355):



$seatNote[$ii] = 1;
$seatName[$ii] = "($from)";
$telephone[$ii] = "Tel: $tel";

These values are written into the data file seat.txt so that the file is updated. The file writing process is defined in the third part of the script as follows:



Listing: Continuation Of The Perl Script ex15-13a.pl (Part Three)

68:
69: open(filehandle,">seat.dir\seat.txt")
70:    or die("Cannot Open Seat.txt File.. Error");
71: flock(filehandle,2) or die("Cannot gain exclusive access.. Error.");
72: print filehandle "$noSeat";
73: for ($ii=1;$ii <= $noSeat;$ii++)
74: {
75:   print filehandle "$seatNote[$ii],$seatName[$ii],$telephone[$ii] \n";
76: }
77: flock(filehandle,8);
78: close(filehandle);

This script opens the data file. As soon as the file is opened, a flock() function is employed to gain exclusive access to the file preventing the danger of concurrent access. If exclusive access is granted, all the data including the total number of seats $noseat, the reservation flags array $seatNote[], and the telephone number array $telephone[] are written into the data file. When the writing is finished, another flock() function is used to release the exclusive access. The updated data file seat.txt can be viewed by a simple click on the Back button.

15.4.3 A simple search engine

Search engines appear almost in every corner of the Web. Have you thought about how to develop a search engine? Even more, have you thought about how to develop a site for users to register their Web sites or pages on your search engine?

Search engines are not the exclusive property of the big players on the Web. You will find that most small groups with special interests have some kind of search engine in one form or another on their sites to communicate information to each other. For example, a small minority group in San Francisco has a Web site with a small search engine to allow users to search other similar groups and Web sites in other cities in the United States. In the next example, we will develop a small search engine to register and search Web sites on pandas.

The interface part of the search engine is a Web page generated by a Perl script.



Example: ex15-14.pl - A Search Engine With Perl

 1: #! /usr/bin/perl
 2: use CGI qw( :standard );
 3: print "Content-type:text/html\n\n";
 4:
 5: my $timeV = scalar(localtime());
 6:
 7: print << "mypage";
 8:  <?xml version="1.0" encoding="iso-8859-1"?>
 9:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
10:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
11:  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
12:  <head><title></title></head>
13:  <style>
14:    .butSt{background:#aaffaa;font-family:arial;font-weight:bold;
15:      font-size:14pt;color:#008800;width:200px;height:30px}
16:    .txtSt{font-weight:bold;font-size:14pt;color:#ffff00}
17:  </style>
18:  <body style="background:#000088;font-family:arial">
19:  <div class="txtSt" style="text-align:center;font-size:18pt">
20:     This Search Engine Is About Pandas<br /><br />
21:     Please Register Your Web Site About Pandas Here
22:
23:   <form action="ex15-14a.pl" method="post">
24:    <table style="width:550px" border="0" class="txtSt" align="center">
25:     <tr><td>Web Site Address (URL):
26:             e.g., www.panda.com/~Robinson/animal/panda.html</td></tr>
27:     <tr><td><input type="text" id="myUrl" name="myUrl" class="butSt"
28:               style="width:500px" /></td></tr>
29:     <tr><td>Site Description: </td></tr>
30:     <tr><td><input type="text" id="des" name="des" class="butSt"
31:               style="width:500px" /></td></tr>
32:    </table><br />
33:     <input type="submit" class="butSt" value="Submit" /> &nbsp;&nbsp;
34:     <input type="reset" class="butSt" value="Reset" />
35:   </form>
36:   <img src="line1.gif" height="6" width="600" /><br /><br/>
37:
38:   <form action="ex15-14b.pl" method="post">
39:       Search The Sites You Want<br /><br />
40:   <table style="width:550px" border="0" class="txtSt" align="center">
41:    <tr><td>Search String:</td><td>
42:      <input type="text" id="searchSt" name="searchSt" class="butSt"
43:         style="width:300px" /></td></tr>
44:   </table><br />
45:     <input type="submit" class="butSt" value="Search" />
46:   </form>
47:
48:   <img src="line1.gif" height="6" width="600" /><br />
49:   Date: $timeV<br />
50:  </div></body></html>
51: mypage

The Web page generated by this Perl script contains two forms. The first form defined in lines 2335 is designed to register a page in the search engine. Once the Web site address (lines 2728) and the description of the site (lines 3031) are filled and the Submit button is pressed, the form is submitted to a Perl script called ex15-14a.pl (line 23) to register the site in the engine.

The purpose of the second form is to search the sites about pandas. A search string is declared in lines 4243. When a keyword is entered and the Search button is clicked, the form is submitted to another Perl script to perform the search. To see how to register your site in the engine, some screen shots are shown in Figs 15.23 and 15.24.

Figure 15.23. ex15-14.p1

graphics/15fig23.jpg


Figure 15.24. A return page for the registration

graphics/15fig24.jpg


The search engine that we are going to develop is a simple text-based engine. The data file is called panda.txt and contains only two fields, namely, the description and the url of the site separated by a special symbol #l#. That is, if you fill in the url and description as



www.pwt-ex.com/johnsmith/animal/panda.html
Just About Anything on Giant Pandas

(as shown in Fig. 15.24), you will add a line at the end of the data file such as



Just About Anything on Giant Pandas#l#www.pwt-ex.com
johnsmith/animal/panda.html

Therefore the registration process is a straightforward operation to append a record to the data file panda.txt. The Perl script is listed below:



Example: ex15-14a.pl - The Perl Script For ex1514.pl

 1: #!usr/bin/perl
 2: use CGI qw (:standard);
 3: print "Content-type:text/html \n\n";
 4:
 5: my $addUrl = param(myUrl);
 6: my $addDes = param(des);
 7: my $timeV = scalar(localtime());
 8:
 9: open(filehandle, ">>panda.txt") or
10:    die("The URL database could not be opened");
11: print filehandle "$addDes#l#$addUrl\n";
12: close (filehandle);
13:
14: print << "mypage";
15:  <?xml version="1.0" encoding="iso-8859-1"?>
16:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
17:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
18:  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
19:  <head><title></title></head>
20:  <style>
21:   .txtSt{font-family:arial;font-weight:bold;font-size:14pt;color:#ffff00}
22:  </style>
23:  <body class="txtSt" style="background:#000088;font-size:18pt;
24:   text-align:center" >
25:     The Following Information Has Been <br />
26:     Added To Our Search Engine
27:     <br />
28:    <img src="line1.gif" width="500" height="6" /><br /><br />
29:  <table class="txtSt" border="0" cellspacing="5">
30:    <tr><td>$addUrl</td></tr>
31:    <tr><td>$addDes</td></tr>
32:  </table>
33:   <br /><img src="line1.gif" height="6" width="500" /><br />
34:   Date: $timeV<br />
35:  </div></body></html>
36: mypage

The returned url and description of the site information are stored in variables $addUrl and $addDes (lines 56). The open statement in line 9 opens the data file panda.txt in append mode. A simple print statement



print filehandle "$addDes#l#$addUrl\n";

would append the description and url of the site at the end of the file panda.txt. The special symbol #1# is used to separate the two fields so that you can split them in the search process. Lines 1436 generate a return page back to the user saying that his or her site has been registered in the engine (see Fig. 15.24).

To see the search process in action, some screen shots are shown in Figs 15.25 and 15.26.

Figure 15.25. Search process

graphics/15fig25.jpg


Figure 15.26. Search results

graphics/15fig26.jpg


When you fill in the search string with a keyword, e.g., "Giant," all the search results about giant pandas will be shown in the returned page as in Fig. 15.26.

The details of the search process are defined in the Perl script below:



Example: ex15-14b.pl - The Perl Script For ex1514.pl

 1: #!usr/bin/perl
 2: use CGI qw (:standard);
 3: print "Content-type:text/html \n\n";
 4:
 5: my $searchSt = param(searchSt);
 6: my $timeV = scalar(localtime());
 7:
 8: print << "mypage";
 9:  <?xml version="1.0" encoding="iso-8859-1"?>
10:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
11:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
12:  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13:  <head><title></title></head>
14:  <style>
15:    .txtSt{font-family:arial;font-weight:bold;font-size:14pt;color:#ffff00}
16:  </style>
17:  <body class="txtSt" style="background:#000088;font-size:18pt;
18:       text-align:center" >
19:     The Search Results Are As Follows:<br />
20:    <img src="line1.gif" width="550" height="6" /><br /><br />
21:
22:  <table class="txtSt" border="0" cellspacing="5">
23: mypage
24:
25: my $count = 0;
26: open(filehandle, "<panda.txt") or
27:    die("The data file cannot be openned .. Error.");
28:
29: while(my $lst = <filehandle>)
30: {
31:   if ($lst =~ /$searchSt/i)
32:   {
33:    $count++;
34:    $lst =~ s/\n//g;
35:    my ($des, $url) = split(/#l#/, $lst);
36:    print "<tr><td valign=\"top\">$count</td><td style=\"width:450px\">
37:            <a href=\"http://$url\" style=\"color:#ffffff\">
38:            http://$url</a></td></tr>";
39:    print "<tr><td></td><td style=\"width:450px\">$des </td></tr>";
40:   }
41: }
42: if ($count ==0)
43: {
44:   print "<tr><td>Sorry ! No Result Has Been Found!</td></tr>";
45: }
46: close (filehandle);
47:
48: print << "endpage";
49: </table><br />
50:   <img src="line1.gif" height="6" width="550" /><br />
51:   Date: $timeV<br />
52:  </div></body></html>
53: endpage

In this script the search string is captured by the variable $searchSt declared in line 5. Lines 823 generate the first part of the returned XHTML page. The second part of the page is more interesting. The open statement in line 26 opens the file panda.txt for reading. For each line of the data, we perform the following tasks:

  • Search the entire line to match the string stored in $searchSt.

  • In case a match is found:

    - Increment the count variable $count by 1.

    - Delete the line break at the end of the line.

    - Split the line into description $des and URL $url.

    - Generate table rows for $des and $url.

In terms of Perl script programming, the tasks above are implemented as a while-loop in lines 2941. If no match is found for the entire data file, a message is returned to the user. Don't forget to close the data file after all the file operations are finished. Another here document is used in lines 4853 to generate the final part of the returned page. Some readers familiar with Perl script may prefer to use Perl script functions to generate headers and other XHTML elements. The implementation here tries to generate a whole, readable, and easy to understand XHTML page.

15.4.4 Accessing an ODBC database with Perl script

A large number of business applications use database techniques. Among both small firms and large corporations, databases are indispensable tools to maintain, manipulate, and access records of data. There are a number of databases on the market, such as Microsoft Access, FoxPro, Borland's Paradox, Sybase, Oracle, and MySQL. Each database has its own application development language and many of them are well integrated with the database itself and incompatible with others. This has created a problem for database developers and users on the Internet.

The Open Database Connectivity (ODBC) is a product from Microsoft and is an integral part of Windows systems such as 9.x/2000/NT/XP. ODBC implements a Structured Query Language (SQL) interface for different databases. The interface takes ODBC function calls and converts them into specific functions for different databases. This feature is convenient and useful. Once a database is registered on a machine with ODBC, people all over the Internet can access the contents and records using the same kind of function calls.

As an example, we consider a Microsoft Access database file people.mdb. This database file contains one table called people. The table has four fields containing information about a group of people as given in Table 15.3.

Table 15.3. Database table: people

ID

Name

Email

Description

1

John

John@.pwt-ex.com

Male, 30 Years Old, London

2

Mary

Mary@.pwt-ex.com

Female, 22 Years Old, Paris

...

...

...

...

10

Anne

Anne@.pwt-ex.com

Female, 30 Years Old, London


To register a database on a Windows system is easy. For example, the following step-by-step procedures can be used to register the database on a Windows XP system:

  • Activate the Control Panel | Administrative Tools (Fig. 15.27). Click on the Data Sources (ODBC) icon to open the "Data Source Administrator" window (Fig. 15.28).

    Figure 15.27. ODBC I

    graphics/15fig27.gif


    Figure 15.28. ODBC II

    graphics/15fig28.gif


  • Click on the System DSN menu and press the Add button to open the "Create New Data Source" window (Fig. 15.29). Select Microsoft Access Drive and click Finish.

    Figure 15.29. ODBC III

    graphics/15fig29.gif


  • The "ODBC Microsoft Access Setup" window will appear (Fig. 15.30). Fill in the Data Source Name and Description as illustrated in Fig. 15.30. Press the Select button and upload the database file people.mdb (Fig. 15.31).

    Figure 15.30. ODBC IV

    graphics/15fig30.gif


    Figure 15.31. ODBC V

    graphics/15fig31.gif


  • Now click the OK button to register the database into the system (Fig. 15.32). You will have the database registered in the window. Now click the OK button to exit.

    Figure 15.32. ODBC VI

    graphics/15fig32.gif


When the information of the DSN and the name of the table is ready, a Web page is developed to access the database using SQL. Chapter 17 is dedicated to databases and the SQL language, but, for now, we use the following simple SQL command to display all records and data of the database:



SELECT * FROM PEOPLE

This statement can be interpreted as "select every field from a table called people." In order to send this SQL out to the ODBC database, we develop a simple XHTML page.



Example: ex15-15.htm - ODBC Database With Perl

 1: <?xml version="1.0" encoding="iso-8859-1"?>
 2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 3:    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 4: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 5: <head><title> ODBC Database With Perl  ex15-15.htm </title></head>
 6: <style>
 7:  .txt{font-family:arial;font-size:14pt;color:#000088;font-weight:bold}
 8:  .butSt{background-color:#aaffaa;font-family:arial;font-weight:bold;
 9:     font-size:14pt;color:#008800;width:520px;height:30px}
10: </style>
11: <body style="font-size:18pt;background:#bbbbff;text-align:center"
12: class="txt"> Access An ODBC DataBase <br />with Perl Script<br /><br />
13:  <img src="line1.gif" width="550" height="6" /><br />
14:
15:  <table class="txt" cellspacing="10" width="550" >
16:   <tr><td colspan="2">Remote Database Information:</td></tr>
17:   <tr valign="top"><td><img src="bullet1.gif" vspace ="3" /></td>
18:     <td>A Microsoft Access DataBase people.mdb is registered
19:        with ODBC </td></tr>
20:   <tr><td valign="top"><img src="bullet1.gif" vspace ="3" /></td>
21:     <td> Data Source Name (DSN) = ex15data</td></tr>
22:   <tr><td valign="top"><img src="bullet1.gif" vspace ="3" /></td>
23:     <td>The database contains a table called people with<br />
24:      ID, Name, Email and Description Fields</td></tr>
25:  </table>
26:
27:  <img src="line1.gif" width="550" height="6" /><br />
28:  <form action = "ex15-15.pl" method="post">
29:     Enter Your SQL Query String<br />
30:   <input type ="text" name = "querySt" size = "120"
31:      value = "select * from people" class="butSt"><br /><br />
32:   <input type = "submit" value = "Send Query" class="butSt"
33:      style="width:180px;background:#bbbbbb">
34:  </form>
35: </body>
36: </html>

After the page header, we have declared a table to describe the ODBC database in lines 1525. In order to use ODBC effectively three pieces of information are needed:

  • the name of the DSN (i.e., ex15data);

  • the name of the database or table (i.e., people);

  • the name of each field in the table (i.e., ID, Name, Email, and Description).

The final part of the page contains a form with a text box and a button. The text box is for the user to type in an SQL command. When the button is pressed, the SQL command or query will be sent to the Perl script ex15-15.pl. The purpose of this script is to connect to the database and collect results. Some screen shots of this page in action are shown in Figs 15.33 and 15.34.

Figure 15.33. ex15-15.htm

graphics/15fig33.jpg


Figure 15.34. ODBC database results

graphics/15fig34.jpg


The SQL query string "SELECT * FROM PEOPLE" would search and display all records of the database. If the following SQL command is used



SELECT Name, Email, Description FROM PEOPLE

you will have a returned page similar to Fig. 15.34 but without the ID column. Since SQL is not case sensitive, you can also use the command



Select * from people where Description like '%Female%'

to search and display all the female records. The SQL clause can be interpreted as



select all fields from the table called people
where the Description field contains a keyword Female.

After SQL, databases on the Web and other related technologies such as ASP and PHP are the central topics of the next two chapters; this example serves as preparation for a more detailed study of each of them. Some more screen shots on SQL in action are shown in Figs 15.35 and 15.36.

Figure 15.35. Another SQL command

graphics/15fig35.jpg


Figure 15.36. ODBC database results

graphics/15fig36.jpg


To connect to the registered ODBC database and collect the results, the following Perl script fragment is used:



Example: ex15-15.pl - The Perl Script For ex1515.htm (Part One)

 1: #!usr/bin/perl
 2:
 3: use Win32::ODBC;
 4: use CGI qw (:standard);
 5: print "Content-type:text/html\n\n";
 6:
 7: my $querySt = param(querySt);
 8: $dsnName = "ex15data";
 9:
10: if (!($odbcData = new Win32::ODBC($dsnName)))
11: {
12:    print "ODBC Connection .. Error..\n";
13:    exit();
14: }
15:
16: if ($odbcData->Sql($querySt))
17: {
18:    print "Error.. SQL failed..\n";
19:    $odbcData->Close();
20:    exit();
21: }
22:

Since ODBC is a Microsoft product and dedicated to the Windows system, an additional Perl module called Win32 is declared in line 3. After that the SQL query string is captured and stored in variable $querySt. The name of the DSN ex15data is also stored in $dsnName. To establish the connection between the script and the database, we execute the statement (see line 10)



$odbcData = new Win32::ODBC($dsnName)

This statement creates a new ODBC object with connection to $dsnName and assigns the object to variable $odbcData. If this statement doesn't return a true value, there is a failure case on the connection.

Once a proper connection is established, you can use the statement



$odbcData->Sql($querySt))

to assign the record set returned by SQL to variable $odbcData. If this process fails, you have an error on the SQL statement and the program needs to be terminated. For example, if you make a mistake by issuing the SQL command "select from peop," the error will be captured by the statements in lines 1621.

The SQL results returned by the database are now available. The second part of the script is to construct a return page back to the user with the SQL records.



Listing: Continuation Of The Perl Script ex15-15.pl (Part Two)

23: print << "mypage";
24:  <?xml version="1.0" encoding="iso-8859-1"?>
25:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
26:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
27:  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
28:  <head><title></title></head>
29:  <style>
30:   .txtSt{font-family:arial;font-weight:bold;font-size:13pt;color:#ffff00}
31:  </style>
32:  <body class="txtSt" style="background:#000088;font-size:18pt;
33:       text-align:center" >
34:   Basic On Your SQL Query String:<br />
35:   <div style="color:#ffffff"> $querySt</div><br />
36:   The Search Results Are As Follows:<br /><br />
37:   <img src="line1.gif" width="600" height="6" alt="pic" /><br /><br />
38:   <table cellspacing="5" class="txtSt" width="570">
39: mypage
40:
41: $count = 0;
42: while($odbcData->FetchRow())
43: {
44:    %odbcData = $odbcData->DataHash();
45:
46:    print "<tr><td>$odbcData{ID}</td><td>$odbcData{Name}</td>
47:          <td>$odbcData{Email}</td><td>$odbcData{Description}</td></tr>";
48:    $count++;
49: }
50: $odbcData->Close();
51:
52: print << "endpage";
53:   </table><br />
54:   <img src="line1.gif" width="600" height="6" alt="pic" /><br /><br />
55:   Total Entries = $count<br /><br />
56:   </body>
57:   </html>
58: endpage

The first part of this script fragment is to generate an XHTML header return to the user. After that a while-loop is used to fetch each row of the SQL records. For each row, the function DataHash() is called to insert the records and individual fields into a hash table (see line 44), i.e.,



%odbcData = $odbcData->DataHash();

Therefore, the variables $odbc{ID}, $odbc{Name}, $odbc{Email}, and $odbc{Description} represent the fields of the database table people and will contain the proper values from the SQL records. The print statement in lines 46-47 formats the values into one table row with four table cells. When all the SQL records are processed, the data source of the object is closed in line 50. The remaining part of the script is to complete the ending of the XHTML page and is easy to understand.

    Table of Contents

    Previous Next