Taking a quiz with Mallard - webquiz.cgi
How do I get to the quiz? How is the quiz program called?
Mallard quizzes are generally made available on the lessons page.
Your lessons page may have various combinations of groups, lessons, and
quizzes on it. You may have to redisplay the lessons page a few times,
opening books and lessons and so on, but eventually you will reach a
portion of the lessons page that has a quiz on it.
The quiz appears on the lessons page as a link, with an icon
for the quiz and the title of that quiz:
WebQuiz: My Quiz Due: 1/1/1999 midnight
The URL the link points to will be something like the following:
HREF='webquiz.cgi?SessionID=my_name_980922_143624&unique=906568209&quiz=my_quiz'
Clicking on the link (the icon or the quiz title) will execute the program
webquiz.cgi, with the following parameters sent to it in the query
string:
- SessionID
- This is a string generated when a user first authenticates and then
passed to every program (by adding it in the URLs on all the links)
in order to allow us to track individual activity sessions. A single
activity session consists of all activity from the first authentication
until the user logs out of Mallard.
- quiz
- The Mallard ID of the quiz.
- unique
- The timestamp for the time the quiz was generated. Adding this to the
URL ensures that the URL is different each time a user takes a
quiz. This allows us to accurately track any variables generated
in the quiz so that the grading will refer to the same variables
used when asking the quiz.
You do not necessarily need to access the quiz from the lessons page. You
may try a quiz from the Quiz Browser or from the Quiz Manager
area. In these areas, quizzes are accessed by filling out and submitting
a form, rather than clicking on a link. However, submitting these forms
calls the same program, webquiz.cgi, as clicking the links on the
lessons page does, with the same arguments.
Taking a quiz always means you are running the script webquiz.cgi.
What does webquiz.cgi do?
The code for webquiz.cgi is given below.
#! /usr/local/bin/perl
# Displays a webquiz.
BEGIN {
use Env qw(MALLARD_HOME);
unshift(@INC, "$MALLARD_HOME/lib");
}
use Mallard;
use Quiz;
$mallard = new Mallard();
if ($Quiz::DEBUG) {
$mallard->print_head("test header for debug");
}
my $webquiz = new Quiz(($mallard->form())->{'quiz'});
$webquiz->display();
$mallard->print_tail("quiz","Taking Mallard Quizzes");
The colored parts of the code above are common (in some form) to
all Mallard CGI scripts. They perform the basic tasks that are
essential for all Mallard CGI scripts. The parts left in plain text
are specific to webquiz.cgi, and deal specifically with presenting
a quiz for the user to fill in and submit.
What are the tasks common to all Mallard CGI scripts?
Before performing their own specific tasks, all Mallard CGI scripts must:
- Locate the Mallard module library used to do all the other tasks.
- Determine the Mallard course area from which it was called
(and get ready to read any files relative to that course's specific
location on the server).
- Determine if the current Mallard license is valid (and restrict
access if it is not).
- Determine the identity of the user.
- Determine the access level of the user (to allow the script to
present the user with different options or perhaps deny access altogether
based on this level).
- Get any information sent in from HTML forms or URLS via CGI
(in other words, "do the CGI stuff").
- Print a valid HTTP header.
Then, after performing any tasks specific to the individual scripts (such
as presenting a quiz for the user to fill in and submit), the scripts must:
- Print the Mallard and course content copyright strings.
- Make the question icon in the icon bar point to the appropriate
help file.
Whew! Believe it or not, the colored lines in the webquiz.cgi code
presented above take care of every one of these tasks. It's that easy.
Each of those lines calls up a lot of detailed functionality,
which will be explained below.
Okay, so what exactly are the colored lines doing?
- Locating the Mallard libraries:
BEGIN {
use Env qw(MALLARD_HOME);
unshift(@INC, "$MALLARD_HOME/lib");
}
- When Perl is installed (by someone with root priviledges) it
installs standard Perl libraries in some standard Perl
locations.
Perl knows to search these standard locations for a given module
when you attempt to use the module in your scripts. The
standard locations are available to the perl scripts in the
special @INC array.
- To use any modules not in those special locations, you must do one
of the following:
- Put your module into one of the standard locations (hard to do
if you aren't root, and generally Not a Good Idea).
- Keep the modules in the same directory as the script itself.
This works because the "." directory ("current directory")
is always part of the @INC array.
- Use a name for the module that matches the relative pathname
to the module from any directory in the @INC array.
- Add the directory where the modules are located into the
@INC array manually.
- Mallard has chosen to add the Mallard module library directory
to the @INC array with the code given above. The contents
of the BEGIN block are executed before the rest of the
file is interpreted (this allows the use statements, which
occur at interpretation time, to work). The line
use Env qw(MALLARD_HOME);
reads the value of the
$MALLARD_HOME environment variable, and the directory
"$MALLARD_HOME/lib" is then added to the @INC array.
- The environment variable $MALLARD_HOME is exported to all
of the Mallard scripts by the web server. This is done by editing
the file obj.conf in the web server configuration
directory, for Netscape servers, and editing the httpd.conf file
for Apache servers. (This is done once by the root user, at the
time the server is initially set up and told to listen to the
Mallard port).
- Incidentally, why do we go to all this trouble? Why not just put
the modules in the same directory as the scripts, or in a subdirectory
of the directory that has the CGI scripts in it?
The reason is that putting the modules underneath the
document root
for the server would allow people with prying eyes to view the
source code, if they were able to guess the module names.
- Authentication, CGI form processing, and locating
the course area:
use Mallard;
$mallard = new Mallard();
...
$mallard->{form}{stuff};
- Now that we have located the libraries, we can instantiate a
Mallard object (using Mallard.pm).
- The Mallard.pm module is a subclass of the Authent.pm
module, which is the main authentication and CGI processing
module for Mallard. The Mallard module simply adds various
printing utilities to the collection of methods it inherits
from Authent.pm. You may use either module, although if the script
does not need the printing utilities, it is better to use Authent.pm
because it takes less time to interpret, which leads to a faster
script execution time.
- Every script must instantiate a Mallard or Authent object.
It is this module which handles all of the authentication and
CGI form processing.
- The Mallard/Authent object MUST be named $mallard. This
$mallard variable must be in package main and not lexically
scoped (basically, don't use "my" for this variable,
though you may, and should, use it for all the other variables in
your script). The reason for these restrictions is that various
other modules which may be instantiated in the scripts (such as
the Question.pm module, for instance) need to be able to refer to
the $mallard object variable and make use of its methods
with "$::mallard" (equivalent to "$main::mallard").
- The Authent->new() method calls several other methods to
perform all of the authentication and CGI form processing before
returning. It also seeds the random number generator. Specifically,
when the Authent->new() call returns, you have access to the
following information stored in various variables on the
$mallard object:
- CGI form data, in $mallard->{form}
- A list of any files uploaded, in
$mallard->{sent_files}
- The contents of any files uploaded, in
$mallard->{sent_data}
- A list of the content types of any files uploaded, in
$mallard->{sent_types}
- The user's login, in $ENV{'REMOTE_USER'}
- The user's actual name, in $mallard->{'USER_NAME'}
- The user's access level, in $mallard->{'USER_TYPE'}
(Note: This is not in a friendly format and should not
be used directly. There are other methods to get at this
information.)
- The user's registered section, in
$mallard->{'USER_SECTION'}
- Various course configuration settings, in
$mallard->{config}
- Various user configuration settings, in
$mallard->{user_config}
- For more information about using Mallard.pm (or Authent.pm) in
your scripts, including details about where and in what
format the above information is stored, and how to use
the Mallard object methods to manipulate it, you should refer
to "Using the Mallard.pm
Object."
(Note: Reading this above page will get you jumpstarted into
writing your own Mallard CGI scripts.)
- For more information about the internals of the Mallard.pm CGI
processing and authentication process, you should refer
to "Authentication and
CGI processing with Mallard.pm - an internal view."
This page is not existing yet - I'm working on it! -Ed.
(Note: Reading this page will give you more background, and
show you exactly how Mallard implements the standard "CGI stuff,"
but is not absolutely necessary for you to begin writing Mallard
CGI scripts.)
- Printing a valid HTTP header at the beginning,
and the appropriate copyright string at the end:
$mallard = new Mallard();
...
$mallard->{form}{stuff};
- All CGI scripts must produce a valid HTTP header before
anything else. Failing to do so will produce a server error.
The minimum header which you must provide is simply
Content-type: text/html
Mallard provides a method $mallard->print_head() which will
not only print the HTTP header, but also print any text
sent to it in a standard page header format, in between
<H1></H1> tags, with optional icons on either side,
and followed by a horizontal rule.
- Although webquiz.cgi seems to be printing a header only
when the $DEBUG variable is set, this is not actually the case.
In fact the Quiz->display() method makes a call to
$::mallard->print_head() before printing anything else to
the page (notice that this is an example of a module making calls
to $main::mallard, which is why you must always name the Mallard.pm
object variable "$mallard"). It is often necessary to print an
extra "test header" as webquiz.cgi does, however, if you will
be printing any debug messages in the code before the
$::mallard->print_head() call is made.
- webquiz.cgi prints both the Mallard software copyright
and the course content copyright at the bottom of the page,
and sets the appropriate help file to be referenced when
the question mark icon is clicked, by using the
$mallard->print_tail() method. This method also prints the
</BODY> and </HTML> tags which finish off
an HTML page.
- More information about both of these methods, including detailed
descriptions of their arguments, may be found in the file
to "Using the Mallard.pm
Object."
What are the other lines referring to Quiz doing?
- Instantiating the quiz:
use Quiz;
my $webquiz = new Quiz($mallard->{form}{'quiz'});
- Recall that $mallard->{form}{'quiz'} contains the quiz
CGI form variable, which in webquiz.cgi contains the
Mallard ID of the quiz.
- The Mallard ID for the quiz is sent to the Quiz.pm constructor.
This Quiz->new() method instantiates various helper objects
and stores them in variables on itself:
- Date.pm: Object to format and compare dates
- GradePolicy.pm: Object which handles the actual grading
of the quiz and storage of the quiz grades
- TypeLoader.pm: Object to load the appropriate input types
so that the quiz questions may call their appropriate asking
and grading methods
- Parser.pm: Object to parse the Mallard Extended HTML
source for both the quiz text (text appearing at the top of the
quiz, above the questions) and the quiz questions
- Quiz->new() then calls Quiz->read_quiz(). This method
does the following:
- Opens the quiz file, and reads its contents.
- Determines whether the quiz is asking or grading (this
depends on an $action variable sent into Quiz->new()
- in the case of webquiz.cgi, where $action is
not set, an action of ASK is assumed). If the quiz is
set to GRADE, it will read the random variables used
when the quiz was asked, and determine the question IDs and
versions used when the quiz was asked. Both of these are
read from temporary files stored on disk in between the
asking and grading programs (webquiz.cgi and
webgrade.cgi).
- Parses the quiz text. Parsing consists of "digesting"
Mallard Extended HTML source, and converting into standard
HTML containing links, forms, and other interactive features.
- Stores information about the questions to be included in each
question pool, how many questions to choose from each
pool, and the weight to use for each question in various
variables on the quiz object.
- Displaying the quiz:
$webquiz->display();
- Once the quiz has been read, Quiz->display() then:
- Prints the quiz header, containing the quiz type name (is
this
a "Homework?" A "WebQuiz?"), and icons indicated in the GradePolicy
for the quiz (do you want little Italian flags on either side?
Little computers? Nothing?) and a horizontal rule.
- Calls Quiz->check_access() to check whether the user
is allowed to access the quiz (students cannot access quizzes
under construction, for example).
- Calls Quiz->choose_versions_to_ask() to randomly choose
which question versions will be used in the quiz. These
question IDs and versions are written to a file which will be
accessed by the grading program (webgrade.cgi) so that it
can grade the same questions that are being asked now.
The questions themselves are instantiated as Question
objects, parsed, and stored in an array on the $webquiz
Quiz object.
Parsing a question, like parsing the quiz, consists of
reading the Mallard Extended HTML source for the question,
extracting various information from it (What input types
does it contain? What are the correct answers? What are the
parameters?) and using that information to construct various
HTML forms and other interactive features on the page.
- Loops through the chosen questions, displaying each
question in turn. (Recall that for each question, the
Quiz object has instantiated a Question object
and parsed it in Quiz->choose_versions_to_ask().) The
Question->ask() method is then called on each
question to display the parsed data for that question as
a set of interactive HTML form elements or as a
Java applet, depending on the input types it contains.
To display the question, the Question object in turn uses
the TypeLoader object sent to it by the Quiz object
to instantiate the appropriate InputType module depending
on the type field in the <input type=foo> tag
in the question source, and calls the InputType->ask()
method on the InputType module.
- Any random variables generated are written to the
disk in a temporary file, where the webgrade.cgi
grading program can read them.
- Whew!
- The end result of the above is a page containing a large HTML
form, which is the interactive Mallard quiz we all know and
love.
- At the bottom of the quiz there is of course a submit button
which the user clicks to submit the quiz for grading.
Submitting the quiz means simply submitting the form generated by
the Quiz->display() method. The action attribute of this
form is set to webgrade.cgi, the quiz grading CGI script.
This grading program is sent the Mallard ID for the quiz,
some uniquifying information, and all of the filled in
form variables that comprise the quiz questions. It then performs
actions similar to those of webquiz.cgi to grade the quiz.
Where can I get more information on Mallard code and library modules?
There is currently no prepared information available for these modules (I do
hope to provide some at some point). However, after reading the above overview
of webquiz.cgi and taking particular note of the method names mentioned
above, you should be able to follow along with the source code for
the modules and scripts, which I've provided below:
Questions? Comments? General harrassment? Mail it to
maiko@wocket.csl.uiuc.edu.