version 1.52, 2002/07/02 21:48:36
|
version 1.55, 2002/07/08 13:38:52
|
Line 48
|
Line 48
|
|
|
=pod |
=pod |
|
|
|
=head1 NAME |
|
|
|
lonchart |
|
|
|
=head1 SYNOPSIS |
|
|
|
Quick display of students grades for a course in a compressed table format. |
|
|
|
=head1 DESCRIPTION |
|
|
|
This module process all student grades for a course and turns them into a |
|
table like structure. |
|
|
|
This is part of the LearningOnline Network with CAPA project |
|
described at http://www.lon-capa.org |
|
|
|
lonchart presents the user with a condensed view all a course's data. The |
|
class title, the number of students, and the date for the last update of the |
|
displayed data. There is also a legend that describes the chart values. |
|
|
|
For each valid grade for a student is linked with a submission record for that |
|
problem. The ability to add and remove columns of data from the chart was |
|
added for reducing the burden of having to scroll through large quantities |
|
of data. The interface also allows for sorting of students by username, |
|
last name, and section number of class. Active and expired students are |
|
also available. |
|
|
|
The interface is controlled by three primary buttons: Recalculate Chart, |
|
Refresh Chart, and Reset Selections. Recalculate Chart will update |
|
the chart to the most recent data and keep the display settings for the chart |
|
the same. Refresh Chart is used to redisplay the chart after selecting |
|
different output formatting. Reset Selections is used to set the chart |
|
display options back to default values. |
|
|
|
=head1 CODE LAYOUT DESCRIPTION |
|
|
|
The code is broken down into five components: formatting data for printing, |
|
downloading data from servers, processing data, helper functions, |
|
and the central processing functions. The module is broken into chunks |
|
for each component. |
|
|
|
=head1 PACKAGES USED |
|
|
|
Apache::Constants qw(:common :http) |
|
Apache::lonnet() |
|
Apache::loncommon() |
|
HTML::TokeParser |
|
GDBM_File |
|
|
=cut |
=cut |
|
|
package Apache::lonchart; |
package Apache::lonchart; |
Line 60 use HTML::TokeParser;
|
Line 109 use HTML::TokeParser;
|
use GDBM_File; |
use GDBM_File; |
|
|
#my $jr; |
#my $jr; |
|
|
|
=pod |
|
|
|
=head1 FORMAT DATA FOR PRINTING |
|
|
|
=cut |
|
|
# ----- FORMAT PRINT DATA ---------------------------------------------- |
# ----- FORMAT PRINT DATA ---------------------------------------------- |
|
|
|
=pod |
|
|
|
=item &FormatStudentInformation() |
|
|
|
This function produces a formatted string of the student's information: |
|
username, domain, section, full name, and PID. |
|
|
|
=over 4 |
|
|
|
Input: $cache, $name, $studentInformation, $spacePadding |
|
|
|
$cache: This is a pointer to a hash that is tied to the cached data |
|
|
|
$name: The name and domain of the current student in name:domain format |
|
|
|
$studentInformation: A pointer to an array holding the names used to |
|
|
|
remove data from the hash. They represent the name of the data to be removed. |
|
|
|
$spacePadding: Extra spaces that represent the space between columns |
|
|
|
Output: $Str |
|
|
|
$Str: Formatted string. |
|
|
|
=back |
|
|
|
=cut |
|
|
sub FormatStudentInformation { |
sub FormatStudentInformation { |
my ($cache,$name,$studentInformation,$spacePadding)=@_; |
my ($cache,$name,$studentInformation,$spacePadding)=@_; |
my $Str=''; |
my $Str=''; |
Line 83 sub FormatStudentInformation {
|
Line 168 sub FormatStudentInformation {
|
return $Str; |
return $Str; |
} |
} |
|
|
|
=pod |
|
|
|
=item &FormatStudentData() |
|
|
|
First, FormatStudentInformation is called and prefixes the course information. |
|
This function produces a formatted string of the student's course information. |
|
Each column of data represents all the problems for a given sequence. For |
|
valid grade data, a link is created for that problem to a submission record |
|
for that problem. |
|
|
|
=over 4 |
|
|
|
Input: $name, $studentInformation, $spacePadding, $ChartDB |
|
|
|
$name: The name and domain of the current student in name:domain format |
|
|
|
$studentInformation: A pointer to an array holding the names used to |
|
remove data from the hash. They represent |
|
the name of the data to be removed. |
|
|
|
$spacePadding: Extra spaces that represent the space between columns |
|
|
|
$ChartDB: The name of the cached data database which will be tied to that |
|
database. |
|
|
|
Output: $Str |
|
|
|
$Str: Formatted string that is an entire row of the chart. It is a |
|
concatenation of student information and student course information. |
|
|
|
=back |
|
|
|
=cut |
|
|
sub FormatStudentData { |
sub FormatStudentData { |
my ($name,$coid,$studentInformation,$spacePadding,$ChartDB)=@_; |
my ($name,$studentInformation,$spacePadding,$ChartDB)=@_; |
my ($sname,$sdom) = split(/\:/,$name); |
my ($sname,$sdom) = split(/\:/,$name); |
my $Str; |
my $Str; |
my %CacheData; |
my %CacheData; |
Line 221 sub FormatStudentData {
|
Line 340 sub FormatStudentData {
|
return $Str; |
return $Str; |
} |
} |
|
|
|
=pod |
|
|
|
=item &CreateTableHeadings() |
|
|
|
This function generates the column headings for the chart. |
|
|
|
=over 4 |
|
|
|
Inputs: $CacheData, $studentInformation, $headings, $spacePadding |
|
|
|
$CacheData: pointer to a hash tied to the cached data database |
|
|
|
$studentInformation: a pointer to an array containing the names of the data |
|
held in a column and is used as part of a key into $CacheData |
|
|
|
$headings: The names of the headings for the student information |
|
|
|
$spacePadding: The spaces to go between columns |
|
|
|
Output: $Str |
|
|
|
$Str: A formatted string of the table column headings. |
|
|
|
=back |
|
|
|
=cut |
|
|
sub CreateTableHeadings { |
sub CreateTableHeadings { |
my ($CacheData,$studentInformation,$headings,$spacePadding)=@_; |
my ($CacheData,$studentInformation,$headings,$spacePadding)=@_; |
my $Str='<pre>'; |
my $Str='<tr>'; |
|
|
for(my $index=0; $index<(scalar @$headings); $index++) { |
for(my $index=0; $index<(scalar @$headings); $index++) { |
if(!&ShouldShowColumn($CacheData, 'heading'.$index)) { |
if(!&ShouldShowColumn($CacheData, 'heading'.$index)) { |
next; |
next; |
} |
} |
|
|
|
$Str .= '<td align="left"><pre>'; |
my $data=$$headings[$index]; |
my $data=$$headings[$index]; |
$Str .= $data; |
$Str .= $data; |
|
|
Line 238 sub CreateTableHeadings {
|
Line 385 sub CreateTableHeadings {
|
$Str .= (' 'x($CacheData->{$$studentInformation[$index].'Length'}- |
$Str .= (' 'x($CacheData->{$$studentInformation[$index].'Length'}- |
$length)); |
$length)); |
$Str .= $spacePadding; |
$Str .= $spacePadding; |
|
$Str .= '</pre></td>'; |
} |
} |
|
|
foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) { |
foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) { |
Line 245 sub CreateTableHeadings {
|
Line 393 sub CreateTableHeadings {
|
next; |
next; |
} |
} |
|
|
|
$Str .= '<td align="left"><pre>'; |
my $name = $CacheData->{$sequence.':title'}; |
my $name = $CacheData->{$sequence.':title'}; |
$Str .= $name; |
$Str .= $name; |
my @titleLength=split(//,$CacheData->{$sequence.':title'}); |
my @titleLength=split(//,$CacheData->{$sequence.':title'}); |
Line 252 sub CreateTableHeadings {
|
Line 401 sub CreateTableHeadings {
|
(scalar @titleLength); |
(scalar @titleLength); |
$Str .= (' 'x$leftover); |
$Str .= (' 'x$leftover); |
$Str .= $spacePadding; |
$Str .= $spacePadding; |
|
$Str .= '</pre></td>'; |
} |
} |
|
|
$Str .= 'Total Solved/Total Problems'; |
$Str .= '<td><pre>Total Solved/Total Problems</pre></td>'; |
$Str .= '</pre>'; |
$Str .= '</tr>'; |
|
|
return $Str; |
return $Str; |
} |
} |
|
|
|
=pod |
|
|
|
=item &CreateColumnSelectionBox() |
|
|
|
If there are columns not being displayed then this selection box is created |
|
with a list of those columns. When selections are made and the page |
|
refreshed, the columns will be removed from this box and the column is |
|
put back in the chart. If there is no columns to select, no row is added |
|
to the interface table. |
|
|
|
=over 4 |
|
Input: $CacheData, $headings |
|
|
|
|
|
$CacheData: A pointer to a hash tied to the cached data |
|
|
|
$headings: An array of the names of the columns for the student information. |
|
They are used for displaying which columns are missing. |
|
|
|
Output: $notThere |
|
|
|
$notThere: The string contains one row of a table. The first column has the |
|
name of the selection box. The second contains the selection box |
|
which has a size of four. |
|
|
|
=back |
|
|
|
=cut |
|
|
sub CreateColumnSelectionBox { |
sub CreateColumnSelectionBox { |
my ($CacheData,$studentInformation,$headings,$spacePadding)=@_; |
my ($CacheData,$headings)=@_; |
|
|
my $missing=0; |
my $missing=0; |
my $notThere='<tr><td align="right"><b>Select column to view:</b>'; |
my $notThere='<tr><td align="right"><b>Select column to view:</b>'; |
Line 295 sub CreateColumnSelectionBox {
|
Line 474 sub CreateColumnSelectionBox {
|
$notThere='<tr><td>'; |
$notThere='<tr><td>'; |
} |
} |
|
|
return $notThere.'</td></tr></tbody></table>'; |
return $notThere.'</td></tr>'; |
} |
} |
|
|
|
=pod |
|
|
|
=item &CreateColumnSelectors() |
|
|
|
This function generates the checkboxes above the column headings. The |
|
column will be removed if the checkbox is unchecked. |
|
|
|
=over 4 |
|
|
|
Input: $CacheData, $headings |
|
|
|
$CacheData: A pointer to a hash tied to the cached data |
|
|
|
$headings: An array of the names of the columns for the student |
|
information. They are used to know what are the student information columns |
|
|
|
Output: $present |
|
|
|
$present: The string contains the first row of a table. Each column contains |
|
a checkbox which is left justified. Currently left justification is used |
|
for consistency of location over the column in which it presides. |
|
|
|
=back |
|
|
|
=cut |
|
|
sub CreateColumnSelectors { |
sub CreateColumnSelectors { |
my ($CacheData,$studentInformation,$headings,$spacePadding)=@_; |
my ($CacheData,$headings)=@_; |
|
|
my $found=0; |
my $found=0; |
my ($name, $length, $position); |
my ($name, $length, $position); |
my $present='<pre>'; |
|
|
my $present = '<tr>'; |
for(my $index=0; $index<(scalar @$headings); $index++) { |
for(my $index=0; $index<(scalar @$headings); $index++) { |
if(!&ShouldShowColumn($CacheData, 'heading'.$index)) { |
if(!&ShouldShowColumn($CacheData, 'heading'.$index)) { |
next; |
next; |
} |
} |
$name = $headings->[$index]; |
$present .= '<td align="left">'; |
$length=$CacheData->{$$studentInformation[$index].'Length'}; |
|
$position=int($length/2); |
|
$present .= (' 'x($position)); |
|
$present .= '<input type="checkbox" checked="on" '; |
$present .= '<input type="checkbox" checked="on" '; |
$present .= 'name="heading'.$index.'">'; |
$present .= 'name="heading'.$index.'" />'; |
$position+=2; |
$present .= '</td>'; |
$present .= (' 'x($length-$position)); |
|
$present .= $spacePadding; |
|
$found++; |
$found++; |
} |
} |
|
|
Line 324 sub CreateColumnSelectors {
|
Line 525 sub CreateColumnSelectors {
|
if(!&ShouldShowColumn($CacheData, 'sequence'.$sequence)) { |
if(!&ShouldShowColumn($CacheData, 'sequence'.$sequence)) { |
next; |
next; |
} |
} |
$name = $CacheData->{$sequence.':title'}; |
$present .= '<td align="left">'; |
$length=$CacheData->{$sequence.':columnWidth'}; |
|
$position=int($length/2); |
|
$present .= (' 'x($position)); |
|
$present .= '<input type="checkbox" checked="on" '; |
$present .= '<input type="checkbox" checked="on" '; |
$present .= 'name="sequence'.$sequence.'">'; |
$present .= 'name="sequence'.$sequence.'" />'; |
$position+=2; |
$present .= '</td>'; |
$present .= (' 'x($length-$position)); |
|
$present .= $spacePadding; |
|
$found++; |
$found++; |
} |
} |
|
|
if($found) { |
if(!$found) { |
$present .= '</pre>'; |
|
$present = $present; |
|
} else { |
|
$present = ''; |
$present = ''; |
} |
} |
|
|
return $present.'</form>'."\n";; |
return $present.'<td></td></tr></form>'."\n";; |
} |
} |
|
|
|
=pod |
|
|
|
=item &CreateForm() |
|
|
|
The interface for this module consists primarily of the controls in this |
|
function. The student status selection (active, expired, any) is set here. |
|
The sort buttons: username, last name, and section are set here. The |
|
other buttons are Recalculate Chart, Refresh Chart, and Reset Selections. |
|
These controls are in a table to clean up the interface. |
|
|
|
=over 4 |
|
|
|
Input: $CacheData |
|
|
|
$CacheData is a hash pointer to tied database for cached data. |
|
|
|
Output: $Ptr |
|
|
|
$Ptr is a string containing all the html for the above mentioned buttons. |
|
|
|
=back |
|
|
|
=cut |
|
|
sub CreateForm { |
sub CreateForm { |
my ($CacheData)=@_; |
my ($CacheData)=@_; |
my $OpSel1=''; |
my $OpSel1=''; |
Line 357 sub CreateForm {
|
Line 574 sub CreateForm {
|
else { $OpSel1 = 'selected'; } |
else { $OpSel1 = 'selected'; } |
|
|
my $Ptr .= '<form name="stat" method="post" action="/adm/chart" >'."\n"; |
my $Ptr .= '<form name="stat" method="post" action="/adm/chart" >'."\n"; |
$Ptr .= '<table border="0"><tbody>'; |
|
$Ptr .= '<tr><td align="right">'; |
$Ptr .= '<tr><td align="right">'; |
$Ptr .= '</td><td align="left">'; |
$Ptr .= '</td><td align="left">'; |
$Ptr .= '<input type="submit" name="recalculate" '; |
$Ptr .= '<input type="submit" name="recalculate" '; |
Line 388 sub CreateForm {
|
Line 604 sub CreateForm {
|
return $Ptr; |
return $Ptr; |
} |
} |
|
|
|
=pod |
|
|
|
=item &CreateLegend() |
|
|
|
This function returns a formatted string containing the legend for the |
|
chart. The legend describes the symbols used to represent grades for |
|
problems. |
|
|
|
=cut |
|
|
sub CreateLegend { |
sub CreateLegend { |
my $Str = "<p><pre>". |
my $Str = "<p><pre>". |
"1..9: correct by student in 1..9 tries\n". |
"1..9: correct by student in 1..9 tries\n". |
Line 402 sub CreateLegend {
|
Line 628 sub CreateLegend {
|
return $Str; |
return $Str; |
} |
} |
|
|
|
=pod |
|
|
|
=item &StartDocument() |
|
|
|
Returns a string containing the header information for the chart: title, |
|
logo, and course title. |
|
|
|
=cut |
|
|
sub StartDocument { |
sub StartDocument { |
my $Str = ''; |
my $Str = ''; |
$Str .= '<html>'; |
$Str .= '<html>'; |
Line 411 sub StartDocument {
|
Line 646 sub StartDocument {
|
$Str .= '<script>window.focus();</script>'; |
$Str .= '<script>window.focus();</script>'; |
$Str .= '<img align=right src=/adm/lonIcons/lonlogos.gif>'; |
$Str .= '<img align=right src=/adm/lonIcons/lonlogos.gif>'; |
$Str .= '<h1>Assessment Chart</h1>'; |
$Str .= '<h1>Assessment Chart</h1>'; |
$Str .= '<h3>'.localtime().'</h3>'; |
|
$Str .= '<h1>'.$ENV{'course.'.$ENV{'request.course.id'}.'.description'}; |
$Str .= '<h1>'.$ENV{'course.'.$ENV{'request.course.id'}.'.description'}; |
$Str .= '</h1>'; |
$Str .= '</h1>'; |
|
|
Line 420 sub StartDocument {
|
Line 654 sub StartDocument {
|
|
|
# ----- END FORMAT PRINT DATA ------------------------------------------ |
# ----- END FORMAT PRINT DATA ------------------------------------------ |
|
|
|
=pod |
|
|
|
=head1 DOWNLOAD INFORMATION |
|
|
|
This section contains all the files that get data from other servers |
|
and/or itself. There is one function that has a call to get remote |
|
information but isn't included here which is ProcessTopLevelMap. The |
|
usage was small enough to be ignored, but that portion may be moved |
|
here in the future. |
|
|
|
=cut |
|
|
# ----- DOWNLOAD INFORMATION ------------------------------------------- |
# ----- DOWNLOAD INFORMATION ------------------------------------------- |
|
|
|
=pod |
|
|
|
=item &DownloadPrerequisiteData() |
|
|
|
Collects lastname, generation, middlename, firstname PID, and section for each |
|
student from their environment database. The list of students is built from |
|
collecting a classlist for the course that is to be displayed. |
|
|
|
=over 4 |
|
|
|
Input: $courseID, $c |
|
|
|
$courseID: The id of the course |
|
|
|
$c: The connection class that can determine if the browser has aborted. It |
|
is used to short circuit this function so that it doesn't continue to |
|
get information when there is no need. |
|
|
|
Output: \%classlist |
|
|
|
\%classlist: A pointer to a hash containing the following data: |
|
|
|
-A list of student name:domain (as keys) (known below as $name) |
|
|
|
-A hash pointer for each student containing lastname, generation, firstname, |
|
middlename, and PID : Key is $name.'studentInformation' |
|
|
|
-A hash pointer to each students section data : Key is $name.section |
|
|
|
=back |
|
|
|
=cut |
|
|
sub DownloadPrerequisiteData { |
sub DownloadPrerequisiteData { |
my ($courseID, $c)=@_; |
my ($courseID, $c)=@_; |
my ($courseDomain,$courseNumber)=split(/\_/,$courseID); |
my ($courseDomain,$courseNumber)=split(/\_/,$courseID); |
Line 462 sub DownloadPrerequisiteData {
|
Line 741 sub DownloadPrerequisiteData {
|
return \%classlist; |
return \%classlist; |
} |
} |
|
|
|
=pod |
|
|
|
=item &DownloadStudentCourseInformation() |
|
|
|
Dump of all the course information for a single student. There is no |
|
pruning of data, it is all stored in a hash and returned. |
|
|
|
=over 4 |
|
|
|
Input: $name, $courseID |
|
|
|
$name: student name:domain |
|
|
|
$courseID: The id of the course |
|
|
|
Output: \%courseData |
|
|
|
\%courseData: A hash pointer to the raw data from the student's course |
|
database. |
|
|
|
=back |
|
|
|
=cut |
|
|
sub DownloadStudentCourseInformation { |
sub DownloadStudentCourseInformation { |
my ($name,$courseID)=@_; |
my ($name,$courseID)=@_; |
my ($studentName,$studentDomain) = split(/\:/,$name); |
my ($studentName,$studentDomain) = split(/\:/,$name); |
Line 474 sub DownloadStudentCourseInformation {
|
Line 777 sub DownloadStudentCourseInformation {
|
|
|
# ----- END DOWNLOAD INFORMATION --------------------------------------- |
# ----- END DOWNLOAD INFORMATION --------------------------------------- |
|
|
# ----- END PROCESSING FUNCTIONS --------------------------------------- |
=pod |
|
|
|
=head1 PROCESSING FUNCTIONS |
|
|
|
These functions process all the data for all the students. Also, they |
|
are the only functions that access the cache database for writing. Thus |
|
they are the only functions that cache data. The downloading and caching |
|
were separated to reduce problems with stopping downloading then can't |
|
tie hash to database later. |
|
|
|
=cut |
|
|
|
# ----- PROCESSING FUNCTIONS --------------------------------------- |
|
|
|
=pod |
|
|
|
=cut |
|
|
sub ProcessTopResourceMap { |
sub ProcessTopResourceMap { |
my ($ChartDB,$c)=@_; |
my ($ChartDB,$c)=@_; |
Line 773 sub ProcessClassList {
|
Line 1092 sub ProcessClassList {
|
$name,$courseID,$c); |
$name,$courseID,$c); |
} |
} |
|
|
|
# Time of download |
|
$CacheData{'time'}=localtime(); |
untie(%CacheData); |
untie(%CacheData); |
} |
} |
|
|
return @names; |
return @names; |
} |
} |
|
|
# ----- END PROCESSING FUNCTIONS --------------------------------------- |
sub ProcessStudentData { |
|
my ($courseData, $name, $ChartDB)=@_; |
|
|
# ----- HELPER FUNCTIONS ----------------------------------------------- |
my %CacheData; |
|
if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) { |
|
my ($checkForError) = keys(%$courseData); |
|
if($checkForError =~ /^(con_lost|error|no_such_host)/i) { |
|
$CacheData{$name.':error'}='Could not download course data.'; |
|
} else { |
|
foreach my $key (keys (%$courseData)) { |
|
$CacheData{$name.':'.$key}=$courseData->{$key}; |
|
} |
|
if(defined($CacheData{'NamesOfStudents'})) { |
|
$CacheData{'NamesOfStudents'}.=':::'.$name; |
|
} else { |
|
$CacheData{'NamesOfStudents'}=$name; |
|
} |
|
} |
|
untie(%CacheData); |
|
} |
|
|
|
return; |
|
} |
|
|
|
=pod |
|
|
|
=item &ProcessFormData() |
|
|
|
Cache form data and set default form data (sort, status, heading.$number, |
|
sequence.$number, reselect, reset, recalculate, and refresh) |
|
|
|
=over 4 |
|
|
|
Input: $ChartDB, $isCached |
|
|
|
$ChartDB: The name of the database for cached data |
|
|
|
$isCached: Is there already data for this course cached. This is used in |
|
conjunction with the absence of all form data to know to display all selection |
|
types. |
|
|
|
Output: None |
|
|
|
=back |
|
|
|
=cut |
|
|
|
sub ProcessFormData { |
|
my ($ChartDB, $isCached)=@_; |
|
my %CacheData; |
|
|
|
if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) { |
|
if(defined($ENV{'form.sort'})) { |
|
$CacheData{'form.sort'}=$ENV{'form.sort'}; |
|
} elsif(!defined($CacheData{'form.sort'})) { |
|
$CacheData{'form.sort'}='username'; |
|
} |
|
|
|
# Ignore $ENV{'form.refresh'} |
|
# Ignore $ENV{'form.recalculate'} |
|
|
|
if(defined($ENV{'form.status'})) { |
|
$CacheData{'form.status'}=$ENV{'form.status'}; |
|
} elsif(!defined($CacheData{'form.status'})) { |
|
$CacheData{'form.status'}='Active'; |
|
} |
|
|
|
my @headings=(); |
|
my @sequences=(); |
|
my $found=0; |
|
foreach (keys(%ENV)) { |
|
if(/form\.heading/) { |
|
$found++; |
|
push(@headings, $_); |
|
} elsif(/form\.sequence/) { |
|
$found++; |
|
push(@sequences, $_); |
|
} elsif(/form\./) { |
|
$found++; |
|
} |
|
} |
|
|
|
if($found) { |
|
$CacheData{'form.headings'}=join(":::",@headings); |
|
$CacheData{'form.sequences'}=join(":::",@sequences); |
|
} |
|
|
|
if(defined($ENV{'form.reselect'})) { |
|
my @reselected = (ref($ENV{'form.reselect'}) ? |
|
@{$ENV{'form.reselect'}} |
|
: ($ENV{'form.reselect'})); |
|
foreach (@reselected) { |
|
if(/heading/) { |
|
$CacheData{'form.headings'}.=":::".$_; |
|
} elsif(/sequence/) { |
|
$CacheData{'form.sequences'}.=":::".$_; |
|
} |
|
} |
|
} |
|
|
|
if(defined($ENV{'form.reset'}) || (!$found && !$isCached)) { |
|
$CacheData{'form.reset'}='true'; |
|
$CacheData{'form.status'}='Active'; |
|
$CacheData{'form.sort'}='username'; |
|
$CacheData{'form.headings'}='ALLHEADINGS'; |
|
$CacheData{'form.sequences'}='ALLSEQUENCES'; |
|
} else { |
|
$CacheData{'form.reset'}='false'; |
|
} |
|
|
|
untie(%CacheData); |
|
} |
|
|
|
return; |
|
} |
|
|
|
=pod |
|
|
|
=item &SpaceColumns() |
|
|
|
Determines the width of all the columns in the chart. It is based on |
|
the max of the data for that column and its header. |
|
|
|
=over 4 |
|
|
|
Input: $students, $studentInformation, $headings, $ChartDB |
|
|
|
$students: An array pointer to a list of students (username:domain) |
|
|
|
$studentInformatin: The type of data for the student information. It is |
|
used as part of the key in $CacheData. |
|
|
|
$headings: The name of the student information columns. |
|
|
|
$ChartDB: The name of the cache database which is opened for read/write. |
|
|
|
Output: None - All data stored in cache. |
|
|
|
=back |
|
|
|
=cut |
|
|
sub SpaceColumns { |
sub SpaceColumns { |
my ($students,$studentInformation,$headings,$ChartDB)=@_; |
my ($students,$studentInformation,$headings,$ChartDB)=@_; |
Line 810 sub SpaceColumns {
|
Line 1269 sub SpaceColumns {
|
return; |
return; |
} |
} |
|
|
|
# ----- END PROCESSING FUNCTIONS --------------------------------------- |
|
|
|
=pod |
|
|
|
=head1 HELPER FUNCTIONS |
|
|
|
These are just a couple of functions do various odd and end |
|
jobs. |
|
|
|
=cut |
|
|
|
# ----- HELPER FUNCTIONS ----------------------------------------------- |
|
|
|
=pod |
|
|
|
=item &ProcessFullName() |
|
|
|
Takes lastname, generation, firstname, and middlename (or some partial |
|
set of this data) and returns the full name version as a string. |
|
|
|
=cut |
|
|
sub ProcessFullName { |
sub ProcessFullName { |
my ($lastname, $generation, $firstname, $middlename)=@_; |
my ($lastname, $generation, $firstname, $middlename)=@_; |
my $Str = ''; |
my $Str = ''; |
Line 850 sub ProcessFullName {
|
Line 1331 sub ProcessFullName {
|
return $Str; |
return $Str; |
} |
} |
|
|
|
=pod |
|
|
|
=item &SortStudents() |
|
|
|
Determines which students to display and in which order. Which are |
|
displayed are determined by their status(active/expired). The order |
|
is determined by the sort button pressed (default to username). The |
|
type of sorting is username, lastname, or section. |
|
|
|
=over 4 |
|
|
|
Input: $students, $CacheData |
|
|
|
$students: A array pointer to a list of students (username:domain) |
|
|
|
$CacheData: A pointer to the hash tied to the cached data |
|
|
|
Output: @order |
|
|
|
@order: An ordered list of students (username:domain) |
|
|
|
=back |
|
|
|
=cut |
|
|
sub SortStudents { |
sub SortStudents { |
my ($students,$CacheData)=@_; |
my ($students,$CacheData)=@_; |
|
|
Line 898 sub SortStudents {
|
Line 1404 sub SortStudents {
|
return @order; |
return @order; |
} |
} |
|
|
|
=pod |
|
|
|
=item &TestCacheData() |
|
|
|
Determine if the cache database can be accessed with a tie. It waits up to |
|
ten seconds before returning failure. This function exists to help with |
|
the problems with stopping the data download. When an abort occurs and the |
|
user quickly presses a form button and httpd child is created. This |
|
child needs to wait for the other to finish (hopefully within ten seconds). |
|
|
|
=over 4 |
|
|
|
Input: $ChartDB |
|
|
|
$ChartDB: The name of the cache database to be opened |
|
|
|
Output: -1, 0, 1 |
|
|
|
-1: Couldn't tie database |
|
0: Use cached data |
|
1: New cache database created, use that. |
|
|
|
=back |
|
|
|
=cut |
|
|
sub TestCacheData { |
sub TestCacheData { |
my ($ChartDB)=@_; |
my ($ChartDB)=@_; |
my $isCached=-1; |
my $isCached=-1; |
Line 932 sub TestCacheData {
|
Line 1464 sub TestCacheData {
|
return $isCached; |
return $isCached; |
} |
} |
|
|
sub ExtractStudentData { |
=pod |
my ($courseData, $name, $ChartDB)=@_; |
|
|
|
my %CacheData; |
=item &ShouldShowColumn() |
if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) { |
|
my ($checkForError) = keys(%$courseData); |
|
if($checkForError =~ /^(con_lost|error|no_such_host)/i) { |
|
$CacheData{$name.':error'}='Could not download course data.'; |
|
} else { |
|
foreach my $key (keys (%$courseData)) { |
|
$CacheData{$name.':'.$key}=$courseData->{$key}; |
|
} |
|
if(defined($CacheData{'NamesOfStudents'})) { |
|
$CacheData{'NamesOfStudents'}.=':::'.$name; |
|
} else { |
|
$CacheData{'NamesOfStudents'}=$name; |
|
} |
|
} |
|
untie(%CacheData); |
|
} |
|
|
|
return; |
Determine if a specified column should be shown on the chart. |
} |
|
|
=over 4 |
|
|
|
Input: $cache, $test |
|
|
|
$cache: A pointer to the hash tied to the cached data |
|
|
|
$test: The form name of the column (heading.$headingIndex) or |
|
(sequence.$sequenceIndex) |
|
|
|
Output: 0 (false), 1 (true) |
|
|
|
=back |
|
|
|
=cut |
|
|
sub ShouldShowColumn { |
sub ShouldShowColumn { |
my ($cache,$test)=@_; |
my ($cache,$test)=@_; |
Line 970 sub ShouldShowColumn {
|
Line 1499 sub ShouldShowColumn {
|
return 1; |
return 1; |
} |
} |
|
|
# my $reselected=$cache->{'form.reselect'}; |
|
# if($reselected=~/$test/) { |
|
# return 1; |
|
# } |
|
|
|
return 0; |
return 0; |
} |
} |
|
|
sub ProcessFormData { |
# ----- END HELPER FUNCTIONS -------------------------------------------- |
my ($ChartDB)=@_; |
|
my %CacheData; |
|
|
|
if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) { |
=pod |
if(defined($ENV{'form.sort'})) { |
|
$CacheData{'form.sort'}=$ENV{'form.sort'}; |
|
} elsif(!defined($CacheData{'form.sort'})) { |
|
$CacheData{'form.sort'}='username'; |
|
} |
|
|
|
# Ignore $ENV{'form.refresh'} |
=head1 Handler and main function(BuildChart) |
# Ignore $ENV{'form.recalculate'} |
|
|
|
if(defined($ENV{'form.status'})) { |
The handler does some initial error checking and then passes the torch to |
$CacheData{'form.status'}=$ENV{'form.status'}; |
BuildChart. BuildChart calls all the appropriate functions to get the |
} elsif(!defined($CacheData{'form.status'})) { |
job done. These are the only two functions that use print ($r). All other |
$CacheData{'form.status'}='Active'; |
functions return strings to BuildChart to be printed. |
} |
|
|
|
my @headings=(); |
=cut |
my @sequences=(); |
|
my $found=0; |
|
foreach (keys(%ENV)) { |
|
if(/form\.heading/) { |
|
$found++; |
|
push(@headings, $_); |
|
} elsif(/form\.sequence/) { |
|
$found++; |
|
push(@sequences, $_); |
|
} elsif(/form\./) { |
|
$found++; |
|
} |
|
} |
|
|
|
if($found) { |
=pod |
$CacheData{'form.headings'}=join(":::",@headings); |
|
$CacheData{'form.sequences'}=join(":::",@sequences); |
|
} |
|
|
|
if(defined($ENV{'form.reselect'})) { |
=item &BuildChart() |
my @reselected = (ref($ENV{'form.reselect'}) ? |
|
@{$ENV{'form.reselect'}} |
|
: ($ENV{'form.reselect'})); |
|
foreach (@reselected) { |
|
if(/heading/) { |
|
$CacheData{'form.headings'}.=":::".$_; |
|
} elsif(/sequence/) { |
|
$CacheData{'form.sequences'}.=":::".$_; |
|
} |
|
} |
|
} |
|
|
|
if(defined($ENV{'form.reset'})) { |
The following is the process that BuildChart goes through to create the |
$CacheData{'form.reset'}='true'; |
html document. |
$CacheData{'form.status'}='Active'; |
|
$CacheData{'form.sort'}='username'; |
|
$CacheData{'form.headings'}='ALLHEADINGS'; |
|
$CacheData{'form.sequences'}='ALLSEQUENCES'; |
|
} else { |
|
$CacheData{'form.reset'}='false'; |
|
} |
|
|
|
untie(%CacheData); |
-Start the lonchart document |
} |
-Test for access to the CacheData |
|
-Download class list information if not using cached data |
|
-Sort students and print out table desciptive data |
|
-Output student data |
|
-If recalculating, store a list of students, but only if all their data was |
|
downloaded. Leave off the others. |
|
-End document |
|
|
return; |
=over 4 |
} |
|
|
|
# ----- END HELPER FUNCTIONS -------------------------------------------- |
Input: $r |
|
|
|
$r: Used to print html |
|
|
|
Output: None |
|
|
|
=back |
|
|
|
=cut |
|
|
sub BuildChart { |
sub BuildChart { |
my ($r)=@_; |
my ($r)=@_; |
Line 1071 sub BuildChart {
|
Line 1565 sub BuildChart {
|
$r->rflush(); |
$r->rflush(); |
return; |
return; |
} |
} |
&ProcessFormData($ChartDB); |
&ProcessFormData($ChartDB, $isCached); |
|
|
# Download class list information if not using cached data |
# Download class list information if not using cached data |
my %CacheData; |
my %CacheData; |
Line 1106 sub BuildChart {
|
Line 1600 sub BuildChart {
|
} |
} |
|
|
# Sort students and print out table desciptive data |
# Sort students and print out table desciptive data |
|
my $downloadTime=0; |
if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_READER,0640)) { |
if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_READER,0640)) { |
if(!$c->aborted()) { @students=&SortStudents(\@students,\%CacheData); } |
if(!$c->aborted()) { @students=&SortStudents(\@students,\%CacheData); } |
|
if(defined($CacheData{'time'})) { $downloadTime=$CacheData{'time'}; } |
|
else { $downloadTime=localtime(); } |
|
if(!$c->aborted()) { $r->print('<h3>'.$downloadTime.'</h3>'); } |
if(!$c->aborted()) { $r->print('<h1>'.(scalar @students). |
if(!$c->aborted()) { $r->print('<h1>'.(scalar @students). |
' students</h1>'); } |
' students</h1>'); } |
if(!$c->aborted()) { $r->rflush(); } |
if(!$c->aborted()) { $r->rflush(); } |
if(!$c->aborted()) { $r->print(&CreateLegend()); } |
if(!$c->aborted()) { $r->print(&CreateLegend()); } |
|
if(!$c->aborted()) { $r->print('<table border="0"><tbody>'); } |
if(!$c->aborted()) { $r->print(&CreateForm(\%CacheData)); } |
if(!$c->aborted()) { $r->print(&CreateForm(\%CacheData)); } |
if(!$c->aborted()) { $r->print(&CreateColumnSelectionBox( |
if(!$c->aborted()) { $r->print(&CreateColumnSelectionBox( |
\%CacheData, |
\%CacheData, |
\@studentInformation, |
\@headings)); } |
\@headings, |
if(!$c->aborted()) { $r->print('</tbody></table>'); } |
$spacePadding)); } |
if(!$c->aborted()) { $r->print('<b>Note: Uncheck the boxes above a'); } |
|
if(!$c->aborted()) { $r->print(' column to remove that column from'); } |
|
if(!$c->aborted()) { $r->print(' the display.</b></pre>'); } |
|
if(!$c->aborted()) { $r->print('<table border="0" cellpadding="0" '); } |
|
if(!$c->aborted()) { $r->print('cellspacing="0"><tbody>'); } |
if(!$c->aborted()) { $r->print(&CreateColumnSelectors( |
if(!$c->aborted()) { $r->print(&CreateColumnSelectors( |
\%CacheData, |
\%CacheData, |
\@studentInformation, |
\@headings)); } |
\@headings, |
|
$spacePadding)); } |
|
if(!$c->aborted()) { $r->print(&CreateTableHeadings( |
if(!$c->aborted()) { $r->print(&CreateTableHeadings( |
\%CacheData, |
\%CacheData, |
\@studentInformation, |
\@studentInformation, |
\@headings, |
\@headings, |
$spacePadding)); } |
$spacePadding)); } |
|
if(!$c->aborted()) { $r->print('</tbody></table>'); } |
if(!$c->aborted()) { $r->rflush(); } |
if(!$c->aborted()) { $r->rflush(); } |
untie(%CacheData); |
untie(%CacheData); |
} else { |
} else { |
Line 1135 sub BuildChart {
|
Line 1637 sub BuildChart {
|
return; |
return; |
} |
} |
|
|
|
# Output student data |
my @updateStudentList = (); |
my @updateStudentList = (); |
my $courseData; |
my $courseData; |
$r->print('<pre>'); |
$r->print('<pre>'); |
Line 1147 sub BuildChart {
|
Line 1650 sub BuildChart {
|
$courseData=&DownloadStudentCourseInformation($_, $cid); |
$courseData=&DownloadStudentCourseInformation($_, $cid); |
if($c->aborted()) { last; } |
if($c->aborted()) { last; } |
push(@updateStudentList, $_); |
push(@updateStudentList, $_); |
&ExtractStudentData($courseData, $_, $ChartDB); |
&ProcessStudentData($courseData, $_, $ChartDB); |
} |
} |
$r->print(&FormatStudentData($_, $cid, \@studentInformation, |
$r->print(&FormatStudentData($_, \@studentInformation, |
$spacePadding, $ChartDB)); |
$spacePadding, $ChartDB)); |
$r->rflush(); |
$r->rflush(); |
} |
} |
|
|
|
# If recalculating, store a list of students, but only if all their |
|
# data was downloaded. Leave off the others. |
if(!$isCached && tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) { |
if(!$isCached && tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) { |
$CacheData{'NamesOfStudents'}=join(":::", @updateStudentList); |
$CacheData{'NamesOfStudents'}=join(":::", @updateStudentList); |
# $CacheData{'NamesOfStudents'}= |
# $CacheData{'NamesOfStudents'}= |
Line 1161 sub BuildChart {
|
Line 1666 sub BuildChart {
|
untie(%CacheData); |
untie(%CacheData); |
} |
} |
|
|
|
# End document |
$r->print('</pre></body></html>'); |
$r->print('</pre></body></html>'); |
$r->rflush(); |
$r->rflush(); |
|
|
Line 1169 sub BuildChart {
|
Line 1675 sub BuildChart {
|
|
|
# ================================================================ Main Handler |
# ================================================================ Main Handler |
|
|
|
=pod |
|
|
|
=item &handler() |
|
|
|
The handler checks for permission to access the course data and for |
|
initial header problem. Then it passes the torch to the work horse |
|
function BuildChart. |
|
|
|
=over 4 |
|
|
|
Input: $r |
|
|
|
$r: This is the object that is used to print. |
|
|
|
Output: A Value (OK or HTTP_NOT_ACCEPTABLE) |
|
|
|
=back |
|
|
|
=cut |
|
|
sub handler { |
sub handler { |
my $r=shift; |
my $r=shift; |
# $jr=$r; |
# $jr=$r; |