![]() ![]() | ![]() |
Fixed a bug in showing the statistics table
1: # The LearningOnline Network with CAPA 2: # (Publication Handler 3: # 4: # $Id: lonstatistics.pm,v 1.36 2002/07/29 21:19:44 minaeibi Exp $ 5: # 6: # Copyright Michigan State University Board of Trustees 7: # 8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA). 9: # 10: # LON-CAPA is free software; you can redistribute it and/or modify 11: # it under the terms of the GNU General Public License as published by 12: # the Free Software Foundation; either version 2 of the License, or 13: # (at your option) any later version. 14: # 15: # LON-CAPA is distributed in the hope that it will be useful, 16: # but WITHOUT ANY WARRANTY; without even the implied warranty of 17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18: # GNU General Public License for more details. 19: # 20: # You should have received a copy of the GNU General Public License 21: # along with LON-CAPA; if not, write to the Free Software 22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23: # 24: # /home/httpd/html/adm/gpl.txt 25: # 26: # http://www.lon-capa.org/ 27: # 28: # (Navigate problems for statistical reports 29: # YEAR=2001 30: # 5/5,7/9,7/25/1,8/11,9/13,9/26,10/5,10/9,10/22,10/26 Behrouz Minaei 31: # 11/1,11/4,11/16,12/14,12/16,12/18,12/20,12/31 Behrouz Minaei 32: # YEAR=2002 33: # 1/22,2/1,2/6,2/25,3/2,3/6,3/17,3/21,3/22,3/26,4/7,5/6 Behrouz Minaei 34: # 5/12,5/14,5/15,5/19,5/26,7/16,25/7,29/7 Behrouz Minaei 35: # 36: ### 37: 38: package Apache::lonstatistics; 39: 40: use strict; 41: use Apache::Constants qw(:common :http); 42: use Apache::lonnet(); 43: use Apache::lonhomework; 44: use Apache::loncommon; 45: use Apache::loncoursedata; 46: use Apache::lonhtmlcommon; 47: use Apache::lonproblemanalysis; 48: use Apache::lonproblemstatistics; 49: use Apache::lonstudentassessment; 50: use Apache::lonchart; 51: use HTML::TokeParser; 52: use GDBM_File; 53: 54: 55: sub CheckFormElement { 56: my ($cache, $ENVName, $cacheName, $default)=@_; 57: 58: if(defined($ENV{'form.'.$ENVName})) { 59: $cache->{$cacheName} = $ENV{'form.'.$ENVName}; 60: } elsif(!defined($cache->{$cacheName})) { 61: $cache->{$cacheName} = $default; 62: } 63: 64: return; 65: } 66: 67: sub ProcessFormData{ 68: my ($cache)=@_; 69: 70: $cache->{'reportKey'} = 'false'; 71: 72: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, 73: ['sort','download', 74: 'reportSelected', 75: 'StudentAssessmentStudent']); 76: &CheckFormElement($cache, 'Status', 'Status', 'Active'); 77: &CheckFormElement($cache, 'postdata', 'reportSelected', 'Class list'); 78: &CheckFormElement($cache, 'reportSelected', 'reportSelected', 79: 'Class list'); 80: $cache->{'reportSelected'} = 81: &Apache::lonnet::unescape($cache->{'reportSelected'}); 82: &CheckFormElement($cache, 'DownloadAll', 'DownloadAll', 'false'); 83: &CheckFormElement($cache, 'sort', 'sort', 'fullname'); 84: &CheckFormElement($cache, 'download', 'download', 'false'); 85: 86: if(defined($ENV{'form.CreateStudentAssessment'}) || 87: defined($ENV{'form.NextStudent'}) || 88: defined($ENV{'form.PreviousStudent'})) { 89: $cache->{'reportSelected'} = 'Student Assessment'; 90: } 91: if(defined($ENV{'form.NextStudent'})) { 92: $cache->{'StudentAssessmentMove'} = 'next'; 93: } elsif(defined($ENV{'form.PreviousStudent'})) { 94: $cache->{'StudentAssessmentMove'} = 'previous'; 95: } else { 96: $cache->{'StudentAssessmentMove'} = 'selected'; 97: } 98: &CheckFormElement($cache, 'StudentAssessmentStudent', 99: 'StudentAssessmentStudent', 'All Students'); 100: $cache->{'StudentAssessmentStudent'} = 101: &Apache::lonnet::unescape($cache->{'StudentAssessmentStudent'}); 102: &CheckFormElement($cache, 'DefaultColumns', 'DefaultColumns', 'false'); 103: 104: if(defined($ENV{'form.Section'})) { 105: my @sectionsSelected = (ref($ENV{'form.Section'}) ? 106: @{$ENV{'form.Section'}} : 107: ($ENV{'form.Section'})); 108: $cache->{'sectionsSelected'} = join(':', @sectionsSelected); 109: } elsif(!defined($cache->{'sectionsSelected'})) { 110: $cache->{'sectionsSelected'} = $cache->{'sectionList'}; 111: } 112: 113: my @headingColumns=(); 114: my @sequenceColumns=(); 115: my $foundColumn = 0; 116: if(defined($ENV{'form.ReselectColumns'})) { 117: my @reselected = (ref($ENV{'form.ReselectColumns'}) ? 118: @{$ENV{'form.ReselectColumns'}} 119: : ($ENV{'form.ReselectColumns'})); 120: foreach (@reselected) { 121: if(/HeadingColumn/) { 122: push(@headingColumns, $_); 123: $foundColumn = 1; 124: } elsif(/SequenceColumn/) { 125: push(@sequenceColumns, $_); 126: $foundColumn = 1; 127: } 128: } 129: } 130: 131: foreach (keys(%ENV)) { 132: if(/form\.Analyze:::/) { 133: # $cache->{'reportSelected'} = 'Analyze'; 134: # $cache->{'reportKey'} = 'Problem Analysis'; 135: my ($uri, $title, $part, $problem); 136: (undef, $uri, $title, $part, $problem)=split(':::', $_); 137: $cache->{'AnalyzeURI'} = $uri; 138: $cache->{'AnalyzeTitle'} = $title; 139: $cache->{'AnalyzePart'} = $part; 140: $cache->{'AnalyzeProblem'} = $problem; 141: 142: &CheckFormElement($cache, 'Interval', 'Interval', '1'); 143: } elsif(/form\.HeadingColumn/) { 144: my $value = $_; 145: $value =~ s/form\.//; 146: push(@headingColumns, $value); 147: $foundColumn=1; 148: } elsif(/form\.SequenceColumn/) { 149: my $value = $_; 150: $value =~ s/form\.//; 151: push(@sequenceColumns, $value); 152: $foundColumn=1; 153: } 154: } 155: 156: if($foundColumn) { 157: $cache->{'HeadingsFound'} = join(':', @headingColumns); 158: $cache->{'SequencesFound'} = join(':', @sequenceColumns);; 159: } 160: if(!defined($cache->{'HeadingsFound'}) || 161: $cache->{'DefaultColumns'} ne 'false') { 162: $cache->{'HeadingsFound'}='HeadingColumnFull Name'; 163: } 164: if(!defined($cache->{'SequencesFound'}) || 165: $cache->{'DefaultColumns'} ne 'false') { 166: $cache->{'SequencesFound'}='All Sequences'; 167: } 168: $cache->{'DefaultColumns'} = 'false'; 169: 170: return; 171: 172: # Select page to display 173: if(defined($ENV{'form.ProblemStatistics'}) || 174: defined($ENV{'form.ProblemStatisticsRecalculate'}) || 175: defined($ENV{'form.DisplayCSVFormat'})) { 176: $cache->{'GoToPage'} = 'ProblemStatistics'; 177: &CheckFormElement($cache, 'DisplayCSVFormat', 178: 'DisplayFormat', 'Display Table Format'); 179: &CheckFormElement($cache, 'Ascend','ProblemStatisticsAscend', 180: 'Ascending'); 181: &CheckFormElement($cache, 'Maps', 'ProblemStatisticsMap', 182: 'All Maps'); 183: } elsif(defined($ENV{'form.ProblemAnalysis'})) { 184: $cache->{'GoToPage'} = 'ProblemAnalysis'; 185: &CheckFormElement($cache, 'Interval', 'Interval', '1'); 186: } elsif(defined($ENV{'form.DoDiffGraph'})) { 187: $cache->{'GoToPage'} = 'DoDiffGraph'; 188: } elsif(defined($ENV{'form.PercentWrongGraph'})) { 189: $cache->{'GoToPage'} = 'PercentWrongGraph'; 190: } elsif(defined($ENV{'form.ActivityLog'})) { 191: $cache->{'GoToPage'} = 'ActivityLog'; 192: } else { 193: $cache->{'GoToPage'} = 'Menu'; 194: } 195: 196: &CheckFormElement($cache, 'Status', 'Status', 'Active'); 197: 198: return; 199: } 200: 201: =pod 202: 203: =item &SortStudents() 204: 205: Determines which students to display and in which order. Which are 206: displayed are determined by their status(active/expired). The order 207: is determined by the sort button pressed (default to username). The 208: type of sorting is username, lastname, or section. 209: 210: =over 4 211: 212: Input: $students, $CacheData 213: 214: $students: A array pointer to a list of students (username:domain) 215: 216: $CacheData: A pointer to the hash tied to the cached data 217: 218: Output: \@order 219: 220: @order: An ordered list of students (username:domain) 221: 222: =back 223: 224: =cut 225: 226: sub SortStudents { 227: my ($cache)=@_; 228: 229: my @students = split(':::',$cache->{'NamesOfStudents'}); 230: my @sorted1Students=(); 231: foreach (@students) { 232: if($cache->{'Status'} eq 'Any' || 233: $cache->{$_.':Status'} eq $cache->{'Status'}) { 234: push(@sorted1Students, $_); 235: } 236: } 237: 238: my $sortBy = ''; 239: if(defined($cache->{'sort'})) { 240: $sortBy = ':'.$cache->{'sort'}; 241: } 242: my @order = sort { $cache->{$a.$sortBy} cmp $cache->{$b.$sortBy} || 243: $cache->{$a.':fullname'} cmp $cache->{$b.':fullname'} } 244: @sorted1Students; 245: 246: return \@order; 247: } 248: 249: =pod 250: 251: =item &SpaceColumns() 252: 253: Determines the width of all the columns in the chart. It is based on 254: the max of the data for that column and its header. 255: 256: =over 4 257: 258: Input: $students, $studentInformation, $headings, $ChartDB 259: 260: $students: An array pointer to a list of students (username:domain) 261: 262: $studentInformatin: The type of data for the student information. It is 263: used as part of the key in $CacheData. 264: 265: $headings: The name of the student information columns. 266: 267: $ChartDB: The name of the cache database which is opened for read/write. 268: 269: Output: None - All data stored in cache. 270: 271: =back 272: 273: =cut 274: 275: sub SpaceColumns { 276: my ($students,$studentInformation,$headings,$cache)=@_; 277: 278: # Initialize Lengths 279: for(my $index=0; $index<(scalar @$headings); $index++) { 280: my @titleLength=split(//,$headings->[$index]); 281: $cache->{$studentInformation->[$index].':columnWidth'}= 282: scalar @titleLength; 283: } 284: 285: foreach my $name (@$students) { 286: foreach (@$studentInformation) { 287: my @dataLength=split(//,$cache->{$name.':'.$_}); 288: my $length=(scalar @dataLength); 289: if($length > $cache->{$_.':columnWidth'}) { 290: $cache->{$_.':columnWidth'}=$length; 291: } 292: } 293: } 294: 295: return; 296: } 297: 298: sub PrepareData { 299: my ($c, $cacheDB, $studentInformation, $headings)=@_; 300: 301: # Test for access to the cache data 302: my $courseID=$ENV{'request.course.id'}; 303: my $isRecalculate=0; 304: if(defined($ENV{'form.Recalculate'})) { 305: $isRecalculate=1; 306: } 307: 308: my $isCached = &Apache::loncoursedata::TestCacheData($cacheDB, 309: $isRecalculate); 310: if($isCached < 0) { 311: return "Unable to tie hash to db file."; 312: } 313: 314: # Download class list information if not using cached data 315: my %cache; 316: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) { 317: return "Unable to tie hash to db file."; 318: } 319: 320: if(!$isCached) { 321: my $processTopResourceMapReturn= 322: &Apache::loncoursedata::ProcessTopResourceMap(\%cache, $c); 323: if($processTopResourceMapReturn ne 'OK') { 324: untie(%cache); 325: return $processTopResourceMapReturn; 326: } 327: } 328: 329: if($c->aborted()) { 330: untie(%cache); 331: return 'aborted'; 332: } 333: 334: my $classlist=&Apache::loncoursedata::DownloadClasslist($courseID, 335: $cache{'ClasslistTimestamp'}, 336: $c); 337: foreach (keys(%$classlist)) { 338: if(/^(con_lost|error|no_such_host)/i) { 339: untie(%cache); 340: return "Error getting student data."; 341: } 342: } 343: 344: if($c->aborted()) { 345: untie(%cache); 346: return 'aborted'; 347: } 348: 349: # Active is a temporary solution, remember to change 350: Apache::loncoursedata::ProcessClasslist(\%cache,$classlist,$courseID,$c); 351: if($c->aborted()) { 352: untie(%cache); 353: return 'aborted'; 354: } 355: 356: &ProcessFormData(\%cache); 357: my $students = &SortStudents(\%cache); 358: &SpaceColumns($students, $studentInformation, $headings, \%cache); 359: $cache{'updateTime:columnWidth'}=24; 360: 361: if($cache{'download'} ne 'false') { 362: my $who = $cache{'download'}; 363: my $courseData = 364: &Apache::loncoursedata::DownloadCourseInformation( 365: $who, $courseID, 366: $cache{$who.':lastDownloadTime'}); 367: &Apache::loncoursedata::ProcessStudentData(\%cache, $courseData, $who); 368: $cache{'download'} = 'false'; 369: } elsif($cache{'DownloadAll'} ne 'false') { 370: my @allStudents; 371: if($cache{'DownloadAll'} eq 'sorted') { 372: @allStudents = @$students; 373: } else { 374: @allStudents = split(':::', $cache{'NamesOfStudents'}); 375: } 376: foreach (@allStudents) { 377: my $courseData = 378: &Apache::loncoursedata::DownloadCourseInformation( 379: $_, $courseID, 380: $cache{$_.':lastDownloadTime'}); 381: &Apache::loncoursedata::ProcessStudentData(\%cache, $courseData, 382: $_); 383: if($c->aborted()) { 384: untie(%cache); 385: return 'aborted'; 386: } 387: } 388: $cache{'DownloadAll'} = 'false'; 389: } 390: 391: if($c->aborted()) { 392: untie(%cache); 393: return 'aborted'; 394: } 395: 396: untie(%cache); 397: 398: return ('OK', $students); 399: } 400: 401: 402: # Create progress 403: sub Create_PrgWin { 404: my ($r)=@_; 405: $r->print(<<ENDPOP); 406: <script> 407: popwin=open('','popwin','width=400,height=100'); 408: popwin.document.writeln('<html><body bgcolor="#88DDFF">'+ 409: '<title>LON-CAPA Statistics</title>'+ 410: '<h4>Computation Progress</h4>'+ 411: '<form name=popremain>'+ 412: '<input type=text size=35 name=remaining value=Starting></form>'+ 413: '</body></html>'); 414: popwin.document.close(); 415: </script> 416: ENDPOP 417: 418: $r->rflush(); 419: } 420: 421: # update progress 422: sub Update_PrgWin { 423: my ($totalStudents,$index,$name,$r)=@_; 424: $r->print('<script>popwin.document.popremain.remaining.value="'. 425: 'Computing '.$index.'/'.$totalStudents.': '. 426: $name.'";</script>'); 427: $r->rflush(); 428: } 429: 430: # close Progress Line 431: sub Close_PrgWin { 432: my ($r)=@_; 433: $r->print('<script>popwin.close()</script>'); 434: $r->rflush(); 435: } 436: 437: 438: sub BuildClasslist { 439: my ($cacheDB,$students,$studentInformation,$headings)=@_; 440: 441: my %cache; 442: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) { 443: return '<html><body>Unable to tie database.</body></html>'; 444: } 445: 446: my $Str=''; 447: $Str .= '<table border="0"><tr><td bgcolor="#777777">'."\n"; 448: $Str .= '<table border="0" cellpadding="3"><tr bgcolor="#e6ffff">'."\n"; 449: 450: my $displayString = '<td align="left"><a href="/adm/statistics?'; 451: $displayString .= 'sort=LINKDATA">DISPLAYDATA </a></td>'."\n"; 452: $Str .= &Apache::lonhtmlcommon::CreateHeadings(\%cache, $studentInformation, 453: $headings, $displayString); 454: $Str .= '</tr>'."\n"; 455: my $alternate=0; 456: foreach (@$students) { 457: my ($username, $domain) = split(':', $_); 458: if($alternate) { 459: $Str .= '<tr bgcolor="#ffffe6">'; 460: } else { 461: $Str .= '<tr bgcolor="#ffffc6">'; 462: } 463: $alternate = ($alternate + 1) % 2; 464: foreach my $data (@$studentInformation) { 465: $Str .= '<td>'; 466: if($data eq 'fullname') { 467: $Str .= '<a href="/adm/statistics?reportSelected='; 468: $Str .= &Apache::lonnet::escape('Student Assessment'); 469: $Str .= '&StudentAssessmentStudent='; 470: $Str .= &Apache::lonnet::escape($cache{$_.':'.$data}).'">'; 471: $Str .= $cache{$_.':'.$data}.' '; 472: $Str .= '</a>'; 473: } elsif($data eq 'updateTime') { 474: $Str .= '<a href="/adm/statistics?reportSelected='; 475: $Str .= &Apache::lonnet::escape('Class list'); 476: $Str .= '&download='.$_.'">'; 477: $Str .= $cache{$_.':'.$data}.' '; 478: $Str .= ' </a>'; 479: } else { 480: $Str .= $cache{$_.':'.$data}.' '; 481: } 482: 483: $Str .= '</td>'."\n"; 484: } 485: } 486: 487: $Str .= '</tr>'."\n"; 488: $Str .= '</table></td></tr></table>'."\n"; 489: 490: untie(%cache); 491: 492: return $Str; 493: } 494: 495: sub CreateMainMenu { 496: my ($status, $reports)=@_; 497: 498: my $Str = ''; 499: 500: $Str .= '<table border="0"><tbody><tr>'."\n"; 501: $Str .= '<td></td><td></td>'."\n"; 502: $Str .= '<td align="center"><b>Analysis Reports:</b></td>'."\n"; 503: $Str .= '<td align="center"><b>Student Status:</b></td></tr>'."\n"; 504: $Str .= '<tr>'."\n"; 505: $Str .= '<td align="center"><input type="submit" name="Refresh" '; 506: $Str .= 'value="Refresh" /></td>'."\n"; 507: $Str .= '<td align="center"><input type="submit" name="DownloadAll" '; 508: $Str .= 'value="Update All Student Data" /></td>'."\n"; 509: $Str .= '<td align="center">'; 510: $Str .= '<select name="reportSelected" onchange="document.'; 511: $Str .= 'Statistics.submit()">'."\n"; 512: 513: foreach (sort(keys(%$reports))) { 514: next if($_ eq 'reportSelected'); 515: $Str .= '<option name="'.$_.'"'; 516: if($reports->{'reportSelected'} eq $reports->{$_}) { 517: $Str .= ' selected=""'; 518: } 519: $Str .= '>'.$reports->{$_}.'</option>'."\n"; 520: } 521: $Str .= '</select></td>'."\n"; 522: 523: $Str .= '<td align="center">'; 524: $Str .= &Apache::lonhtmlcommon::StatusOptions($status, 'Statistics'); 525: $Str .= '</td>'."\n"; 526: 527: $Str .= '</tr></tbody></table>'."\n"; 528: $Str .= '<hr>'."\n"; 529: 530: return $Str; 531: } 532: 533: sub BuildStatistics { 534: my ($r)=@_; 535: 536: my $c = $r->connection; 537: my @studentInformation=('fullname','section','id','domain','username', 538: 'updateTime'); 539: my @headings=('Full Name', 'Section', 'PID', 'Domain', 'User Name', 540: 'Last Updated'); 541: my $spacing = ' '; 542: my %reports = ('classlist' => 'Class list', 543: 'problem_statistics' => 'Problem Statistics', 544: 'student_assessment' => 'Student Assessment', 545: 'reportSelected' => 'Class list'); 546: 547: my %cache; 548: my $courseID=$ENV{'request.course.id'}; 549: my $cacheDB = "/home/httpd/perl/tmp/$ENV{'user.name'}". 550: "_$ENV{'user.domain'}_$courseID\_statistics.db"; 551: 552: my ($returnValue, $students) = &PrepareData($c, $cacheDB, 553: \@studentInformation, 554: \@headings); 555: if($returnValue ne 'OK') { 556: $r->print('<html><body>'.$returnValue."\n".'</body></html>'); 557: return OK; 558: } 559: 560: my $GoToPage; 561: if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) { 562: $GoToPage = $cache{'reportSelected'}; 563: $reports{'reportSelected'} = $cache{'reportSelected'}; 564: # if(defined($cache{'reportKey'}) && $cache{'reportKey'} ne 'false') { 565: # $reports{$cache{'reportKey'}} = $cache{'reportSelected'}; 566: # } 567: 568: if(defined($cache{'OptionResponses'})) { 569: $reports{'problem_analysis'} = 'Problem Analysis'; 570: } 571: 572: $r->print(&Apache::lonhtmlcommon::Title('LON-CAPA Statistics')); 573: $r->print('<form name="Statistics" '); 574: $r->print('method="post" action="/adm/statistics">'); 575: $r->print(&CreateMainMenu($cache{'Status'}, \%reports)); 576: untie(%cache); 577: } else { 578: $r->print('<html><body>Unable to tie database.</body></html>'); 579: return OK; 580: } 581: 582: if($GoToPage eq 'Activity Log') { 583: &Apache::lonproblemstatistics::Activity(); 584: } elsif($GoToPage eq 'Problem Statistics') { 585: &Apache::lonproblemstatistics::BuildProblemStatisticsPage($cacheDB, 586: $students, 587: $courseID, 588: $c,$r); 589: } elsif($GoToPage eq 'Problem Analysis') { 590: $r->print( 591: &Apache::lonproblemanalysis::BuildProblemAnalysisPage($cacheDB)); 592: } elsif($GoToPage eq 'Student Assessment') { 593: $r->print( 594: &Apache::lonstudentassessment::BuildStudentAssessmentPage($cacheDB, 595: $students, 596: $courseID, 597: 'Statistics', 598: \@headings, 599: $spacing, 600: \@studentInformation, 601: $r, $c)); 602: } elsif($GoToPage eq 'Analyze') { 603: $r->print(&Apache::lonproblemanalysis::BuildAnalyzePage($cacheDB, 604: $students, 605: $courseID)); 606: } elsif($GoToPage eq 'DoDiffGraph') { 607: &Apache::lonproblemstatistics::BuildDiffGraph($r); 608: } elsif($GoToPage eq 'PercentWrongGraph') { 609: &Apache::lonproblemstatistics::BuildWrongGraph($r); 610: } elsif($GoToPage eq 'Class list') { 611: $r->print(&BuildClasslist($cacheDB, $students, \@studentInformation, 612: \@headings)); 613: } 614: 615: $r->print('</form>'."\n"); 616: $r->print("\n".'</body>'."\n".'</html>'); 617: $r->rflush(); 618: 619: return OK; 620: } 621: 622: # ================================================================ Main Handler 623: 624: sub handler { 625: my $r=shift; 626: 627: # $jr = $r; 628: 629: unless(&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) { 630: $ENV{'user.error.msg'}= 631: $r->uri.":vgr:0:0:Cannot view grades for complete course"; 632: return HTTP_NOT_ACCEPTABLE; 633: } 634: 635: # Set document type for header only 636: if($r->header_only) { 637: if ($ENV{'browser.mathml'}) { 638: $r->content_type('text/xml'); 639: } else { 640: $r->content_type('text/html'); 641: } 642: &Apache::loncommon::no_cache($r); 643: $r->send_http_header; 644: return OK; 645: } 646: 647: unless($ENV{'request.course.fn'}) { 648: my $requrl=$r->uri; 649: $ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized"; 650: return HTTP_NOT_ACCEPTABLE; 651: } 652: 653: $r->content_type('text/html'); 654: $r->send_http_header; 655: 656: &BuildStatistics($r); 657: 658: return OK; 659: } 660: 1; 661: __END__ 662: