File:  [LON-CAPA] / loncom / interface / lonstatistics.pm
Revision 1.60: download - view: text, annotated - select for diffs
Tue Feb 25 20:47:47 2003 UTC (21 years, 3 months ago) by matthew
Branches: MAIN
CVS tags: HEAD
lonstatistics.pm:
   POD cleanups
   @SelectedSections is set when we read the classlist.
   Added variables $top_map, @Sequences, and @Assessments.  These are
      initialized by &PrepareCourseData($r).
   Added &PrepareCourseData($r) which is essentially a wrapper for
      &Apache::loncoursedata::get_sequence_assessment_data().
   Added &MapSelect() to output a <select> box for sequences.  Not tested.
   Added &SectionSelect(..) to output a <select> box for sections.  Tested.
   Added &DisplayClasslist($r) which displays a table of the current classlist
      that is sortable by each column.  Does not bother with 'update time' as
      this should not be an issue for the user.  Tested.
   Added call to &PrepareCourseData by the handler.
lonpercentage.pm, lonproblemanalysis.pm, lonproblemstatistics.pm,
lonstudentassessment.pm: modified to call &Apache::lonstatistics::SectionSelect
   instead of the method in lonhtmlcommon.pm.

    1: # The LearningOnline Network with CAPA
    2: #
    3: # $Id: lonstatistics.pm,v 1.60 2003/02/25 20:47:47 matthew Exp $
    4: #
    5: # Copyright Michigan State University Board of Trustees
    6: #
    7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    8: #
    9: # LON-CAPA is free software; you can redistribute it and/or modify
   10: # it under the terms of the GNU General Public License as published by
   11: # the Free Software Foundation; either version 2 of the License, or
   12: # (at your option) any later version.
   13: #
   14: # LON-CAPA is distributed in the hope that it will be useful,
   15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17: # GNU General Public License for more details.
   18: #
   19: # You should have received a copy of the GNU General Public License
   20: # along with LON-CAPA; if not, write to the Free Software
   21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   22: #
   23: # /home/httpd/html/adm/gpl.txt
   24: #
   25: # http://www.lon-capa.org/
   26: #
   27: # (Navigate problems for statistical reports
   28: #
   29: ###
   30: 
   31: =pod
   32: 
   33: =head1 NAME
   34: 
   35: lonstatistics
   36: 
   37: =head1 SYNOPSIS
   38: 
   39: Main handler for statistics and chart.
   40: 
   41: =head1 PACKAGES USED
   42: 
   43:     use strict;
   44:     use Apache::Constants qw(:common :http);
   45:     use Apache::lonnet();
   46:     use Apache::lonhomework;
   47:     use Apache::loncommon;
   48:     use Apache::loncoursedata;
   49:     use Apache::lonhtmlcommon;
   50:     use Apache::lonproblemanalysis;
   51:     use Apache::lonproblemstatistics;
   52:     use Apache::lonstudentassessment;
   53:     use Apache::lonpercentage;
   54:     use GDBM_File;
   55: 
   56: =over 4
   57: 
   58: =cut
   59: 
   60: package Apache::lonstatistics;
   61: 
   62: use strict;
   63: use Apache::Constants qw(:common :http);
   64: use Apache::lonnet();
   65: use Apache::lonhomework;
   66: use Apache::loncommon;
   67: use Apache::loncoursedata;
   68: use Apache::lonhtmlcommon;
   69: use Apache::lonproblemanalysis;
   70: use Apache::lonproblemstatistics;
   71: use Apache::lonstudentassessment;
   72: use Apache::lonpercentage;
   73: use GDBM_File;
   74: 
   75: use vars qw/@FullClasslist @Students @Sections @SelectedSections
   76:     $curr_student $prev_student $next_student 
   77:     $top_map @Sequences @Assessments /;
   78: 
   79: #######################################################
   80: #######################################################
   81: 
   82: =pod
   83: 
   84: =item Package Variables
   85: 
   86: =item @FullClasslist The full classlist
   87: 
   88: =item @Students The students we are concerned with for this invocation
   89: 
   90: =item @Sections The sections available in this class
   91: 
   92: =item $curr_student The student currently being examined
   93: 
   94: =item $prev_student The student previous in the classlist
   95: 
   96: =item $next_student The student next in the classlist
   97: 
   98: =over
   99: 
  100: =cut 
  101: 
  102: #######################################################
  103: #######################################################
  104: #
  105: # Classlist variables
  106: #
  107: my @FullClasslist;
  108: my @Students;
  109: my @Sections;
  110: my @SelectedSections;
  111: my $curr_student;
  112: my $prev_student;
  113: my $next_student;
  114: 
  115: #######################################################
  116: #######################################################
  117: 
  118: =pod
  119: 
  120: =item &clear_classlist_variables()
  121: 
  122: undef the following package variables:
  123: 
  124: =over
  125: 
  126: =item @FullClasslist
  127: 
  128: =item @Students
  129: 
  130: =item @Sections
  131: 
  132: =item @SelectedSections
  133: 
  134: =item $curr_student
  135: 
  136: =item $prev_student
  137: 
  138: =item $next_student
  139: 
  140: =back
  141: 
  142: =cut
  143: 
  144: #######################################################
  145: #######################################################
  146: sub clear_classlist_variables {
  147:     undef(@FullClasslist);
  148:     undef(@Students);
  149:     undef(@Sections);
  150:     undef(@SelectedSections);
  151:     undef($curr_student);
  152:     undef($prev_student);
  153:     undef($next_student);
  154: }
  155: 
  156: #######################################################
  157: #######################################################
  158: 
  159: =pod
  160: 
  161: =item &PrepareClasslist()
  162: 
  163: Build up the classlist information.  The classlist information is kept in
  164: the following package variables:
  165: 
  166: =over
  167: 
  168: =item @FullClasslist
  169: 
  170: =item @Students
  171: 
  172: =item @Sections
  173: 
  174: =item @SelectedSections
  175: 
  176: =item $curr_student
  177: 
  178: =item $prev_student
  179: 
  180: =item $next_student
  181: 
  182: =back
  183: 
  184: $curr_student, $prev_student, and $next_student may not be defined, depending
  185: upon the calling context.
  186: 
  187: =cut
  188: 
  189: #######################################################
  190: #######################################################
  191: sub PrepareClasslist {
  192:     my $r = shift;
  193:     my %Sections;
  194:     &clear_classlist_variables();
  195:     #
  196:     # Retrieve the classlist
  197:     my $cid  = $ENV{'request.course.id'};
  198:     my $cdom = $ENV{'course.'.$cid.'.domain'};
  199:     my $cnum = $ENV{'course.'.$cid.'.num'};
  200:     my ($classlist,$field_names) = &Apache::loncoursedata::get_classlist($cid,
  201:                                                                   $cdom,$cnum);
  202:     if (exists($ENV{'form.Section'})) {
  203:         if (ref($ENV{'form.Section'})) {
  204:             @SelectedSections = @$ENV{'form.Section'};
  205:             # Remove the empty sections
  206:             for (my $i=0; $i<=$#SelectedSections; $i++) {
  207:                 if ($SelectedSections[$i] =~ /^\s*$/) {
  208:                     splice(@SelectedSections,$i,1);
  209:                 }
  210:             }
  211:         } else {
  212:             if ($ENV{'form.Section'} !~ /^\s*$/) {
  213:                 @SelectedSections = ($ENV{'form.Section'});
  214:             }
  215:         }
  216:     }
  217:     @SelectedSections = ('any') if (! @SelectedSections);
  218:     #
  219:     # Process the classlist
  220:     while (my ($student,$student_data) = each (%$classlist)) {
  221:         my $studenthash = ();
  222:         for (my $i=0; $i< scalar(@$field_names);$i++) {
  223:             $studenthash->{$field_names->[$i]}=$student_data->[$i];
  224:         }
  225:         push (@FullClasslist,$studenthash);
  226:         #
  227:         # Build up a list of sections
  228:         my $section = $studenthash->{'section'};
  229:         if (! defined($section) || $section =~/^\s*$/ || $section == -1) {
  230:             $studenthash->{'section'} = 'none';
  231:             $section = $studenthash->{'section'};
  232:         }
  233:         $Sections{$section}++;
  234:         #
  235:         # Only put in the list those students we are interested in
  236:         foreach my $sect (@SelectedSections) {
  237:             if (($sect eq 'any') || ($section eq $sect)) {
  238:                 push (@Students,$studenthash);
  239:                 last;
  240:             }
  241:         }
  242:     }
  243:     #
  244:     # Put the consolidated section data in the right place
  245:     @Sections = sort {$a cmp $b} keys(%Sections);
  246:     #
  247:     # Sort the Students
  248:     my $sortby = 'fullname';
  249:     $sortby = $ENV{'form.sort'} if (exists($ENV{'form.sort'}));
  250:     my @TmpStudents = sort { $a->{$sortby} cmp $b->{$sortby} ||
  251:                              $a->{'fullname'} cmp $b->{'fullname'} } @Students;
  252: 
  253:     @Students = @TmpStudents;
  254:     # 
  255:     # Now deal with that current student thing....
  256:     if (exists($ENV{'form.StudentAssessmentStudent'})) {
  257:         my ($current_uname,$current_dom) = 
  258:             split(':',$ENV{'form.StudentAssessmentStudent'});
  259:         my $i;
  260:         for ($i = 0; $i<=$#Students; $i++) {
  261:             next if (($Students[$i]->{'username'} ne $current_uname) || 
  262:                      ($Students[$i]->{'domain'}   ne $current_dom));
  263:             $curr_student = $Students[$i];
  264:             last; # If we get here, we have our student.
  265:         }
  266:         if ($i == 0) {
  267:             $prev_student = 'none';
  268:         } else {
  269:             $prev_student = $Students[$i-1];
  270:         }
  271:         if ($i == $#Students) {
  272:             $next_student = 'none';
  273:         } else {
  274:             $next_student = $Students[$i+1];
  275:         }
  276:     }
  277: }
  278: 
  279: #######################################################
  280: #######################################################
  281: #
  282: # Course Sequences variables
  283: #
  284: my $top_map;
  285: my @Sequences;
  286: my @Assessments;
  287: 
  288: #######################################################
  289: #######################################################
  290: 
  291: =pod
  292: 
  293: =item &clear_sequence_variables()
  294: 
  295: =cut
  296: 
  297: #######################################################
  298: #######################################################
  299: sub clear_sequence_variables {
  300:     undef($top_map);
  301:     undef(@Sequences);
  302:     undef(@Assessments);
  303: }
  304: 
  305: #######################################################
  306: #######################################################
  307: 
  308: =pod
  309: 
  310: =item &PrepareCourseData($r)
  311: 
  312: =cut
  313: 
  314: #######################################################
  315: #######################################################
  316: sub PrepareCourseData {
  317:     my ($r) = @_;
  318:     &clear_sequence_variables();
  319:     my ($top,$sequences,$assessments) = &Apache::loncoursedata::get_sequence_assessment_data();
  320:     if (! defined($top) || ! ref($top)) {
  321:         # There has been an error, better report it
  322:         &Apache::lonnet::logthis('top is undefined');
  323:         return;
  324:     }
  325:     $top_map = $top if (ref($top));
  326:     @Sequences = @{$sequences} if (ref($sequences) eq 'ARRAY');
  327:     @Assessments = @{$assessments} if (ref($assessments) eq 'HASH');
  328: 
  329: =pod
  330: 
  331:     ##
  332:     ## Debugging code
  333:     ##
  334:     foreach my $s (@Sequences) {
  335:         next if ($s->{'title'} ne 'Bioenergetics: Enzyme Regulation');
  336:         &Apache::lonnet::logthis('-----------------------------------');
  337:         &Apache::lonnet::logthis('title      = '.$s->{'title'});
  338:         &Apache::lonnet::logthis('symb       = '.$s->{'symb'});
  339:         &Apache::lonnet::logthis('num_assess = '.$s->{'num_assess'});
  340:         foreach my $a (@{$s->{'contents'}}) {
  341:             &Apache::lonnet::logthis('   --------------------------------');
  342:             &Apache::lonnet::logthis('   title      = '.$a->{'title'});
  343:             &Apache::lonnet::logthis('   symb       = '.$a->{'symb'});
  344:         }
  345:     }
  346: 
  347: =cut
  348: 
  349:     return;
  350: }
  351: 
  352: ##############################################
  353: ##############################################
  354: 
  355: =pod 
  356: 
  357: =item &MapSelect($elementname,$status,$numvisible,$selected,$restriction) 
  358: 
  359: Returns html for a selection box allowing the user to choose one (or more) 
  360: of the sequences in the course.  The values of the sequences are the symbs.
  361: If the top sequence is selected, the value 'top' will result.
  362: 
  363: =over 4
  364: 
  365: =item $elementname The name of the HTML form element
  366: 
  367: =item $status 'multiple' or 'single' selection box
  368: 
  369: =item $numvisible The number of options to be visible
  370: 
  371: =item $selected Array ref to the names of the already selected maps.
  372: If undef, $ENV{'form.'.$elementname} is used.  
  373: If $ENV{'form.'.$elementname} is also empty, none will be selected.
  374: 
  375: =item $restriction Code reference to subroutine which returns true or 
  376: false.  The code must expect a reference to a sequence data structure.
  377: 
  378: =back
  379: 
  380: =cut
  381: 
  382: ##############################################
  383: ##############################################
  384: sub MapSelect {
  385:     my ($elementname,$status,$numvisible,$selected,$restriction)=@_;
  386:     if ($numvisible < 1) {
  387:         return;
  388:     }
  389:     #
  390:     # Set up array of selected items
  391:     my @Selected;
  392:     if (! defined($selected)) {
  393:         if (exists($ENV{'form.'.$elementname})) {
  394:             if (ref($ENV{'form.'.$elementname})) {
  395:                 @Selected = @$ENV{'form.'.$elementname};
  396:             } else {
  397:                 @Selected = ($ENV{'form.'.$elementname});
  398:             }
  399:         } else {
  400:             @Selected = ();
  401:         }
  402:     } else {
  403:         if (ref($selected)) {
  404:             @Selected = @$selected;
  405:         } else {
  406:             @Selected = ($selected);
  407:         }
  408:     }
  409:     #
  410:     # Set up the restriction call
  411:     if (! defined($restriction)) {
  412:         $restriction = sub { 1; };
  413:     }
  414:     #
  415:     # Build the form element
  416:     my $Str = "\n";
  417:     $Str .= '<select name="'.$elementname.'" ';
  418:     if ($status ne 'single') {
  419:         $Str .= 'multiple="true" ';
  420:     }
  421:     $Str .= 'size="'.$numvisible.'" >'."\n";
  422:     #
  423:     # Loop through the sequences
  424:     foreach my $s (@Sequences) {
  425:         next if (! $restriction->($s));
  426:         $Str .= '    <option value="'.$s->{'symb'}.'" ';
  427:         foreach (@Selected) {
  428:             if ($s->{'symb'} eq $_) {
  429:                 $Str .= 'selected ';
  430:                 last;
  431:             }
  432:         }
  433:         $Str .= '>'.$s->{'title'}."</option>\n";
  434:     }
  435:     $Str .= "</select>\n";
  436:     return $Str;
  437: }
  438: 
  439: 
  440: ##############################################
  441: ##############################################
  442: 
  443: =pod 
  444: 
  445: =item &SectionSelect($elementname,$status,$numvisible) 
  446: 
  447: Returns html for a selection box allowing the user to choose one (or more) 
  448: of the sections in the course.  
  449: 
  450: =over 4
  451: 
  452: =item $elementname The name of the HTML form element
  453: 
  454: =item $status 'multiple' or 'single' selection box
  455: 
  456: =item $numvisible The number of options to be visible
  457: 
  458: =item $selected Array ref to the names of the already selected sections.
  459: If undef, $ENV{'form.'.$elementname} is used.  
  460: If $ENV{'form.'.$elementname} is also empty, none will be selected.
  461: 
  462: =item $restriction Code reference to subroutine which returns true or 
  463: false.  The code must expect a reference to a sequence data structure.
  464: 
  465: =back
  466: 
  467: =cut
  468: 
  469: ##############################################
  470: ##############################################
  471: sub SectionSelect {
  472:     my ($elementname,$status,$numvisible)=@_;
  473:     if ($numvisible < 1) {
  474:         return;
  475:     }
  476:     #
  477:     # Build the form element
  478:     my $Str = "\n";
  479:     $Str .= '<select name="'.$elementname.'" ';
  480:     if ($status ne 'single') {
  481:         $Str .= 'multiple="true" ';
  482:     }
  483:     $Str .= 'size="'.$numvisible.'" >'."\n";
  484:     #
  485:     # Loop through the sequences
  486:     foreach my $s (@Sections) {
  487:         $Str .= '    <option value="'.$s.'" ';
  488:         foreach (@SelectedSections) {
  489:             if ($s eq $_ || $_ =~ /^(any|all)$/) {
  490:                 $Str .= 'selected ';
  491:                 last;
  492:             }
  493:         }
  494:         $Str .= '>'.$s."</option>\n";
  495:     }
  496:     $Str .= "</select>\n";
  497:     return $Str;
  498: }
  499: 
  500: ##############################################
  501: ##############################################
  502: 
  503: sub CheckFormElement {
  504:     my ($cache, $ENVName, $cacheName, $default)=@_;
  505: 
  506:     if(defined($ENV{'form.'.$ENVName})) {
  507:         $cache->{$cacheName} = $ENV{'form.'.$ENVName};
  508:     } elsif(!defined($cache->{$cacheName})) {
  509:         $cache->{$cacheName} = $default;
  510:     } else {
  511:         $ENV{'form.'.$ENVName} = $cache->{$cacheName};
  512:     }
  513:     return;
  514: }
  515: 
  516: sub ProcessFormData{
  517:     my ($cache)=@_;
  518: 
  519:     $cache->{'reportKey'} = 'false';
  520: 
  521:     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
  522:                                             ['download',
  523:                                              'reportSelected',
  524:                                              'StudentAssessmentStudent',
  525:                                              'ProblemStatisticsSort']);
  526:     &CheckFormElement($cache, 'DownloadAll', 'DownloadAll', 'false');
  527:     if ($cache->{'DownloadAll'} ne 'false') {
  528:         # Clean the hell out of that cache!
  529:         # We cannot untie the hash at this scope (stupid libgd :( )
  530:         # So, remove every single key.  What a waste of time....
  531:         # Of course, if you are doing this you are probably resigned
  532:         # to waiting a while.
  533:         &Apache::lonnet::logthis("Cleaning out the cache file");
  534:         while (my ($key,undef)=each(%$cache)) {
  535:             next if ($key eq 'DownloadAll');
  536:             delete($cache->{$key});
  537:         }
  538:     }
  539:     &CheckFormElement($cache, 'Status', 'Status', 'Active');
  540:     &CheckFormElement($cache, 'postdata', 'reportSelected', 'Class list');
  541:     &CheckFormElement($cache, 'reportSelected', 'reportSelected', 
  542:                       'Class list');
  543:     $cache->{'reportSelected'} = 
  544:         &Apache::lonnet::unescape($cache->{'reportSelected'});
  545:     &CheckFormElement($cache, 'sort', 'sort', 'fullname');
  546:     &CheckFormElement($cache, 'download', 'download', 'false');
  547:     &CheckFormElement($cache, 'StatisticsMaps', 
  548:                       'StatisticsMaps', 'All Maps');
  549:     &CheckFormElement($cache, 'StatisticsProblemSelect',
  550: 		      'StatisticsProblemSelect', 'All Problems');
  551:     &CheckFormElement($cache, 'StatisticsPartSelect',
  552: 		      'StatisticsPartSelect', 'All Parts');
  553:     if(defined($ENV{'form.Section'})) {
  554:         my @sectionsSelected = (ref($ENV{'form.Section'}) ?
  555:                                @{$ENV{'form.Section'}} :
  556:                                 ($ENV{'form.Section'}));
  557:         $cache->{'sectionsSelected'} = join(':', @sectionsSelected);
  558:     } elsif(!defined($cache->{'sectionsSelected'})) {
  559:         $cache->{'sectionsSelected'} = $cache->{'sectionList'};
  560:     }
  561: 
  562:     # student assessment
  563:     if(defined($ENV{'form.CreateStudentAssessment'}) ||
  564:        defined($ENV{'form.NextStudent'}) ||
  565:        defined($ENV{'form.PreviousStudent'})) {
  566:         $cache->{'reportSelected'} = 'Student Assessment';
  567:     }
  568:     if(defined($ENV{'form.NextStudent'})) {
  569:         $cache->{'StudentAssessmentMove'} = 'next';
  570:     } elsif(defined($ENV{'form.PreviousStudent'})) {
  571:         $cache->{'StudentAssessmentMove'} = 'previous';
  572:     } else {
  573:         $cache->{'StudentAssessmentMove'} = 'selected';
  574:     }
  575:     &CheckFormElement($cache, 'StudentAssessmentStudent', 
  576:                       'StudentAssessmentStudent', 'All Students');
  577:     $cache->{'StudentAssessmentStudent'} = 
  578:         &Apache::lonnet::unescape($cache->{'StudentAssessmentStudent'});
  579:     &CheckFormElement($cache, 'DefaultColumns', 'DefaultColumns', 'false');
  580: 
  581:     # Problem analysis
  582:     &CheckFormElement($cache, 'Interval', 'Interval', '1');
  583: 
  584:     # ProblemStatistcs
  585:     &CheckFormElement($cache, 'DisplayCSVFormat',
  586:                       'DisplayFormat', 'Display Table Format');
  587:     &CheckFormElement($cache, 'ProblemStatisticsAscend',
  588:                       'ProblemStatisticsAscend', 'Ascending');
  589:     &CheckFormElement($cache, 'ProblemStatisticsSort',
  590:                       'ProblemStatisticsSort', 'Homework Sets Order');
  591:     &CheckFormElement($cache, 'DisplayLegend', 'DisplayLegend', 
  592: 		      'Hide Legend');
  593:     &CheckFormElement($cache, 'SortProblems', 'SortProblems', 
  594:                       'Sort Within Sequence');
  595: 
  596:     # Search only form elements
  597:     my @headingColumns=();
  598:     my @sequenceColumns=();
  599:     my $foundColumn = 0;
  600:     if(defined($ENV{'form.ReselectColumns'})) {
  601:         my @reselected = (ref($ENV{'form.ReselectColumns'}) ? 
  602:                           @{$ENV{'form.ReselectColumns'}}
  603:                           : ($ENV{'form.ReselectColumns'}));
  604:         foreach (@reselected) {
  605:             if(/HeadingColumn/) {
  606:                 push(@headingColumns, $_);
  607:                 $foundColumn = 1;
  608:             } elsif(/SequenceColumn/) {
  609:                 push(@sequenceColumns, $_);
  610:                 $foundColumn = 1;
  611:             }
  612:         }
  613:     }
  614: 
  615:     $cache->{'reportKey'} = 'false';
  616:     if($cache->{'reportSelected'} eq 'Analyze') {
  617:         $cache->{'reportKey'} = 'Analyze';
  618:     } elsif($cache->{'reportSelected'} eq 'DoDiffGraph') {
  619:         $cache->{'reportKey'} = 'DoDiffGraph';
  620:     } elsif($cache->{'reportSelected'} eq 'PercentWrongGraph') {
  621:         $cache->{'reportKey'} = 'PercentWrongGraph';
  622:     }
  623: 
  624:     if(defined($ENV{'form.DoDiffGraph'})) {
  625:         $cache->{'reportSelected'} = 'DoDiffGraph';
  626:         $cache->{'reportKey'} = 'DoDiffGraph';
  627:     } elsif(defined($ENV{'form.PercentWrongGraph'})) {
  628:         $cache->{'reportSelected'} = 'PercentWrongGraph';
  629:         $cache->{'reportKey'} = 'PercentWrongGraph';
  630:     }
  631: 
  632:     foreach (keys(%ENV)) {
  633:         if(/form\.Analyze/) {
  634:             $cache->{'reportSelected'} = 'Analyze';
  635:             $cache->{'reportKey'} = 'Analyze';
  636:             my $data;
  637:             (undef, $data)=split(':::', $_);
  638:             $cache->{'AnalyzeInfo'}=$data;
  639:         } elsif(/form\.HeadingColumn/) {
  640:             my $value = $_;
  641:             $value =~ s/form\.//;
  642:             push(@headingColumns, $value);
  643:             $foundColumn=1;
  644:         } elsif(/form\.SequenceColumn/) {
  645:             my $value = $_;
  646:             $value =~ s/form\.//;
  647:             push(@sequenceColumns, $value);
  648:             $foundColumn=1;
  649:         }
  650:     }
  651: 
  652:     if($foundColumn) {
  653:         $cache->{'HeadingsFound'} = join(':', @headingColumns);
  654:         $cache->{'SequencesFound'} = join(':', @sequenceColumns);;
  655:     }
  656:     if(!defined($cache->{'HeadingsFound'}) || 
  657:        $cache->{'DefaultColumns'} ne 'false') {
  658:         $cache->{'HeadingsFound'}='HeadingColumnFull Name';
  659:     }
  660:     if(!defined($cache->{'SequencesFound'}) ||
  661:        $cache->{'DefaultColumns'} ne 'false') {
  662:         $cache->{'SequencesFound'}='All Sequences';
  663:     }
  664:     $cache->{'DefaultColumns'} = 'false';
  665: 
  666:     return;
  667: }
  668: 
  669: =pod
  670: 
  671: =item &SortStudents()
  672: 
  673: Determines which students to display and in which order.  Which are 
  674: displayed are determined by their status(active/expired).  The order
  675: is determined by the sort button pressed (default to username).  The
  676: type of sorting is username, lastname, or section.
  677: 
  678: =over 4
  679: 
  680: Input: $students, $CacheData
  681: 
  682: $students: A array pointer to a list of students (username:domain)
  683: 
  684: $CacheData: A pointer to the hash tied to the cached data
  685: 
  686: Output: \@order
  687: 
  688: @order: An ordered list of students (username:domain)
  689: 
  690: =back
  691: 
  692: =cut
  693: 
  694: sub SortStudents {
  695:     my ($cache)=@_;
  696: 
  697:     my @students = split(':::',$cache->{'NamesOfStudents'});
  698:     my @sorted1Students=();
  699:     foreach (@students) {
  700:         if($cache->{'Status'} eq 'Any' || 
  701:            $cache->{$_.':Status'} eq $cache->{'Status'}) {
  702:             push(@sorted1Students, $_);
  703:         }
  704:     }
  705: 
  706:     my $sortBy = '';
  707:     if(defined($cache->{'sort'})) {
  708:         $sortBy = ':'.$cache->{'sort'};
  709:     } else {
  710:         $sortBy = ':fullname';
  711:     }
  712:     my @order = sort { lc($cache->{$a.$sortBy}) cmp lc($cache->{$b.$sortBy}) ||
  713:                        lc($cache->{$a.':fullname'}) cmp lc($cache->{$b.':fullname'}) } 
  714:                 @sorted1Students;
  715: 
  716:     return \@order;
  717: }
  718: 
  719: =pod
  720: 
  721: =item &SpaceColumns()
  722: 
  723: Determines the width of all the columns in the chart.  It is based on
  724: the max of the data for that column and its header.
  725: 
  726: =over 4
  727: 
  728: Input: $students, $studentInformation, $headings, $ChartDB
  729: 
  730: $students: An array pointer to a list of students (username:domain)
  731: 
  732: $studentInformatin: The type of data for the student information.  It is
  733: used as part of the key in $CacheData.
  734: 
  735: $headings: The name of the student information columns.
  736: 
  737: $ChartDB: The name of the cache database which is opened for read/write.
  738: 
  739: Output: None - All data stored in cache.
  740: 
  741: =back
  742: 
  743: =cut
  744: 
  745: sub SpaceColumns {
  746:     my ($students,$studentInformation,$headings,$cache)=@_;
  747: 
  748:     # Initialize Lengths
  749:     for(my $index=0; $index<(scalar @$headings); $index++) {
  750:         my @titleLength=split(//,$headings->[$index]);
  751:         $cache->{$studentInformation->[$index].':columnWidth'}=
  752:             scalar @titleLength;
  753:     }
  754: 
  755:     foreach my $name (@$students) {
  756:         foreach (@$studentInformation) {
  757:             my @dataLength=split(//,$cache->{$name.':'.$_});
  758:             my $length=(scalar @dataLength);
  759:             if($length > $cache->{$_.':columnWidth'}) {
  760:                 $cache->{$_.':columnWidth'}=$length;
  761:             }
  762:         }
  763:     }
  764: 
  765:     return;
  766: }
  767: 
  768: sub PrepareData {
  769:     my ($c, $cacheDB, $studentInformation, $headings,$r)=@_;
  770: 
  771:     # Test for access to the cache data
  772:     my $courseID=$ENV{'request.course.id'};
  773:     my $isRecalculate=0;
  774:     if(defined($ENV{'form.Recalculate'})) {
  775:         $isRecalculate=1;
  776:     }
  777: 
  778:     my $isCached = &Apache::loncoursedata::TestCacheData($cacheDB,
  779:                                                          $isRecalculate);
  780:     if($isCached < 0) {
  781:         return "Unable to tie hash to db file.";
  782:     }
  783: 
  784:     # Download class list information if not using cached data
  785:     my %cache;
  786:     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT(),0640)) {
  787:         return "Unable to tie hash to db file.";
  788:     }
  789: 
  790: #    if(!$isCached) {
  791:         my $processTopResourceMapReturn=
  792:             &Apache::loncoursedata::ProcessTopResourceMap(\%cache, $c);
  793:         if($processTopResourceMapReturn ne 'OK') {
  794:             untie(%cache);
  795:             return $processTopResourceMapReturn;
  796:         }
  797:  #   }
  798: 
  799:     if($c->aborted()) {
  800:         untie(%cache);
  801:         return 'aborted'; 
  802:     }
  803: 
  804:     my $classlist=&Apache::loncoursedata::DownloadClasslist($courseID,
  805:                                                 $cache{'ClasslistTimestamp'},
  806:                                                 $c);
  807:     foreach (keys(%$classlist)) {
  808:         if(/^(con_lost|error|no_such_host)/i) {
  809:             untie(%cache);
  810:             return "Error getting student data.";
  811:         }
  812:     }
  813: 
  814:     if($c->aborted()) {
  815:         untie(%cache);
  816:         return 'aborted'; 
  817:     }
  818: 
  819:     # Active is a temporary solution, remember to change
  820:     Apache::loncoursedata::ProcessClasslist(\%cache,$classlist,$courseID,$c);
  821:     if($c->aborted()) {
  822:         untie(%cache);
  823:         return 'aborted'; 
  824:     }
  825: 
  826:     &ProcessFormData(\%cache);
  827:     my $students = &SortStudents(\%cache);
  828:     &SpaceColumns($students, $studentInformation, $headings, \%cache);
  829:     $cache{'updateTime:columnWidth'}=24;
  830: 
  831:     my $download = $cache{'download'};
  832:     my $downloadAll = $cache{'DownloadAll'};
  833:     my @allStudents=();
  834:     if($download ne 'false') {
  835:         $cache{'download'} = 'false';
  836:     } elsif($downloadAll ne 'false') {
  837:         $cache{'DownloadAll'} = 'false';
  838:         if($downloadAll eq 'sorted') {
  839:             @allStudents = @$students;
  840:         } else {
  841:             @allStudents = split(':::', $cache{'NamesOfStudents'});
  842:         }
  843:     }
  844: 
  845:     untie(%cache);
  846: 
  847:     if($download ne 'false') {
  848:         my @who = ($download);
  849:         if(&Apache::loncoursedata::DownloadStudentCourseData(\@who, 'false',
  850:                                                              $cacheDB, 'true',
  851:                                                              'false', $courseID,
  852:                                                              $r, $c) ne 'OK') {
  853:             return 'Stop at download individual';
  854:         }
  855:     } elsif($downloadAll ne 'false') {
  856:         if(&Apache::loncoursedata::DownloadStudentCourseData(\@allStudents,
  857:                                                              'false',
  858:                                                              $cacheDB, 'true',
  859:                                                              'true', $courseID,
  860:                                                              $r, $c) ne 'OK') {
  861:             return 'Stop at download all';
  862:         }
  863:     }
  864: 
  865:     return ('OK', $students);
  866: }
  867: 
  868: sub DisplayClasslist {
  869:     my ($r)=@_;
  870:     #
  871:     my @Fields = ('fullname','username','domain','id','section');
  872:     #
  873:     my $Str='';
  874:     $Str .= '<table border="0"><tr><td bgcolor="#777777">'."\n";
  875:     $Str .= '<table border="0" cellpadding="3"><tr bgcolor="#e6ffff">'."\n";
  876:     foreach my $field (@Fields) {
  877:         $Str .= '<th><a href="/adm/statistics?sort='.$field.'">'.$field.
  878:             '</a></th>';
  879:     }
  880:     $Str .= '</tr>'."\n";
  881:     #
  882:     my $alternate = 0;
  883:     foreach my $student (@Students) {
  884:         my $sname = $student->{'username'}.':'.$student->{'domain'};
  885:         if($alternate) {
  886:             $Str .= '<tr bgcolor="#ffffe6">';
  887:         } else {
  888:             $Str .= '<tr bgcolor="#ffffc6">';
  889:         }
  890:         $alternate = ($alternate + 1) % 2;
  891:         #
  892:         foreach my $field (@Fields) {
  893:             $Str .= '<td>';
  894:             if ($field eq 'fullname') {
  895:                 $Str .= '<a href="/adm/statistics?reportSelected=';
  896:                 $Str .= &Apache::lonnet::escape('Student Assessment');
  897:                 $Str .= '&StudentAssessmentStudent=';
  898:                 $Str .= &Apache::lonnet::escape($student->{$field}).'">';
  899:                 $Str .= $student->{$field}.'&nbsp';
  900:                 $Str .= '</a>';
  901:             } else {
  902:                 $Str .= $student->{$field};
  903:             }
  904:             $Str .= '</td>';
  905:         }
  906:         $Str .= "</tr>\n";
  907:     }
  908:     $Str .= '</table></td></tr></table>'."\n";
  909:     #
  910:     $r->print($Str);
  911:     $r->rflush();
  912:     #
  913:     return;
  914: }
  915: 
  916: sub BuildClasslist {
  917:     my ($cacheDB,$students,$studentInformation,$headings,$r)=@_;
  918: 
  919:     my %cache;
  920:     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
  921:         return '<html><body>Unable to tie database.</body></html>';
  922:     }
  923: 
  924: #    my $Ptr = '';
  925: #    $Ptr .= '<table border="0"><tbody>';
  926: #    $Ptr .= '<tr><td align="right"><b>Select Sections</b>';
  927: #    $Ptr .= '</td>'."\n";
  928: #    $Ptr .= '<td align="left">'."\n";
  929: #    my @sectionsSelected = split(':',$cache{'sectionsSelected'});
  930: #    my @sections = split(':',$cache{'sectionList'});
  931: #    $Ptr .= &Apache::lonhtmlcommon::MultipleSectionSelect(\@sections,
  932: #                                                          \@sectionsSelected,
  933: #                                                          'Statistics');
  934: #    $Ptr .= '</td></tr></table><br>';
  935: #    $r->print($Ptr);
  936: #    $r->rflush();
  937: #    my %mySections = ();
  938: #    foreach (@sections) { $mySections{$_} = 'True'; }
  939: #    $r->print("<br>$cache{'sectionsSelected'}<br>");
  940: 
  941:     my $Str='';
  942:     $Str .= '<table border="0"><tr><td bgcolor="#777777">'."\n";
  943:     $Str .= '<table border="0" cellpadding="3"><tr bgcolor="#e6ffff">'."\n";
  944: 
  945:     my $displayString = '<td align="left"><a href="/adm/statistics?';
  946:     $displayString .= 'sort=LINKDATA">DISPLAYDATA&nbsp</a></td>'."\n";
  947:     $Str .= &Apache::lonhtmlcommon::CreateHeadings(\%cache,
  948:                                                    $studentInformation,
  949:                                                    $headings, $displayString);
  950:     $Str .= '</tr>'."\n";
  951: 
  952:     my $alternate=0;
  953:     foreach (@$students) {
  954: #        if ($mySections{$cache{$_.':'.'section'}} ne 'True') {next;}
  955:         my ($username, $domain) = split(':', $_);
  956:         if($alternate) {
  957:             $Str .= '<tr bgcolor="#ffffe6">';
  958:         } else {
  959:             $Str .= '<tr bgcolor="#ffffc6">';
  960:         }
  961:         $alternate = ($alternate + 1) % 2;
  962:         foreach my $data (@$studentInformation) {
  963:             $Str .= '<td>';
  964:             if($data eq 'fullname') {
  965:                 $Str .= '<a href="/adm/statistics?reportSelected=';
  966:                 $Str .= &Apache::lonnet::escape('Student Assessment');
  967:                 $Str .= '&StudentAssessmentStudent=';
  968:                 $Str .= &Apache::lonnet::escape($cache{$_.':'.$data}).'">';
  969:                 $Str .= $cache{$_.':'.$data}.'&nbsp';
  970:                 $Str .= '</a>';
  971:             } elsif($data eq 'updateTime') {
  972:                 $Str .= '<a href="/adm/statistics?reportSelected=';
  973:                 $Str .= &Apache::lonnet::escape('Class list');
  974:                 $Str .= '&download='.$_.'">';
  975:                 $Str .= $cache{$_.':'.$data}.'&nbsp';
  976:                 $Str .= '&nbsp</a>';
  977:             } else {
  978:                 $Str .= $cache{$_.':'.$data}.'&nbsp';
  979:             }
  980: 
  981:             $Str .= '</td>'."\n";
  982:         }
  983:     }
  984: 
  985:     $Str .= '</tr>'."\n";
  986:     $Str .= '</table></td></tr></table>'."\n";
  987:     $r->print($Str);
  988:     $r->rflush();
  989: 
  990:     untie(%cache);
  991: 
  992:     return;
  993: }
  994: 
  995: sub CreateMainMenu {
  996:     my ($status, $reports)=@_;
  997: 
  998:     my $Str = '';
  999: 
 1000:     $Str .= '<table border="0"><tbody><tr>'."\n";
 1001:     $Str .= '<td></td><td></td>'."\n";
 1002:     $Str .= '<td align="center"><b>Select a Report</b></td>'."\n";
 1003:     $Str .= '<td align="center"><b>Student Status</b></td></tr>'."\n";
 1004:     $Str .= '<tr>'."\n";
 1005:     $Str .= '<td align="center"><input type="submit" name="Refresh" ';
 1006:     $Str .= 'value="Refresh" /></td>'."\n";
 1007:     $Str .= '<td align="center"><input type="submit" name="DownloadAll" ';
 1008:     $Str .= 'value="Update All Student Data" /></td>'."\n";
 1009:     $Str .= '<td align="center">';
 1010:     $Str .= '<select name="reportSelected" onchange="document.';
 1011:     $Str .= 'Statistics.submit()">'."\n";
 1012: 
 1013:     foreach (sort(keys(%$reports))) {
 1014:         next if($_ eq 'reportSelected');
 1015:         $Str .= '<option name="'.$_.'"';
 1016:         if($reports->{'reportSelected'} eq $reports->{$_}) {
 1017:             $Str .= ' selected=""';
 1018:         }
 1019:         $Str .= '>'.$reports->{$_}.'</option>'."\n";
 1020:     }
 1021:     $Str .= '</select></td>'."\n";
 1022: 
 1023:     $Str .= '<td align="center">';
 1024:     $Str .= &Apache::lonhtmlcommon::StatusOptions($status, 'Statistics');
 1025:     $Str .= '</td>'."\n";
 1026: 
 1027:     $Str .= '</tr></tbody></table>'."\n";
 1028:     $Str .= '<hr>'."\n";
 1029: 
 1030:     return $Str;
 1031: }
 1032: 
 1033: sub BuildStatistics {
 1034:     my ($r)=@_;
 1035: 
 1036:     my $c = $r->connection;
 1037:     my @studentInformation=('fullname','section','id','domain','username',
 1038:                             'updateTime');
 1039:     my @headings=('Full Name', 'Section', 'PID', 'Domain', 'User Name',
 1040:                   'Last Updated');
 1041:     my $spacing = '   ';
 1042: 
 1043:     my %reports = ('classlist'          => 'Class list',
 1044:                    'problem_statistics' => 'Problem Statistics',
 1045:                    'student_assessment' => 'Student Assessment',
 1046: 		   'percentage'         => 'Correct-problems Plot',
 1047: #                   'activitylog'        => 'Activity Log',
 1048:                    'reportSelected'     => 'Class list');
 1049: 
 1050:     my %cache;
 1051:     my $courseID=$ENV{'request.course.id'};
 1052:     my $cacheDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".
 1053:                   "_$ENV{'user.domain'}_$courseID\_statistics.db";
 1054: 
 1055:     $r->print(&Apache::lonhtmlcommon::Title('Course Statistics and Charts'));
 1056: 
 1057:     my ($returnValue, $students) = &PrepareData($c, $cacheDB,
 1058:                                                 \@studentInformation,
 1059:                                                 \@headings,$r);
 1060:     if($returnValue ne 'OK') {
 1061:         $r->print($returnValue."\n".'</body></html>');
 1062:         return OK;
 1063:     }
 1064:     if(!$c->aborted()) {
 1065:         &Apache::loncoursedata::CheckForResidualDownload($cacheDB,
 1066:                                                          'true', 'true',
 1067:                                                          $courseID,
 1068:                                                          $r, $c);
 1069:     }
 1070: 
 1071:     my $GoToPage;
 1072:     if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
 1073:         $GoToPage = $cache{'reportSelected'};
 1074:         $reports{'reportSelected'} = $cache{'reportSelected'};
 1075:         if(defined($cache{'reportKey'}) &&
 1076:            !exists($reports{$cache{'reportKey'}}) &&
 1077:            $cache{'reportKey'} ne 'false') {
 1078:             $reports{$cache{'reportKey'}} = $cache{'reportSelected'};
 1079:         }
 1080: 
 1081:         if(defined($cache{'OptionResponses'})) {
 1082:             $reports{'problem_analysis'} = 'Option Response Analysis';
 1083:         }
 1084: 
 1085:         $r->print('<form name="Statistics" ');
 1086:         $r->print('method="post" action="/adm/statistics">');
 1087:         $r->print(&CreateMainMenu($cache{'Status'}, \%reports));
 1088:         $r->rflush();
 1089:         untie(%cache);
 1090:     } else {
 1091:         $r->print('<html><body>Unable to tie database.</body></html>');
 1092:         return OK;
 1093:     }
 1094: 
 1095:     if($GoToPage eq 'Activity Log') {
 1096:         &Apache::lonproblemstatistics::Activity();
 1097:     } elsif($GoToPage eq 'Problem Statistics') {
 1098:         &Apache::lonproblemstatistics::BuildProblemStatisticsPage($cacheDB,
 1099:                                                                   $students,
 1100:                                                                   $courseID,
 1101:                                                                   $c,$r);
 1102:     } elsif($GoToPage eq 'Option Response Analysis') {
 1103:         &Apache::lonproblemanalysis::BuildProblemAnalysisPage($cacheDB, $r);
 1104:     } elsif($GoToPage eq 'Student Assessment') {
 1105:         &Apache::lonstudentassessment::BuildStudentAssessmentPage($cacheDB,
 1106:                                                           $students,
 1107:                                                           $courseID,
 1108:                                                           'Statistics',
 1109:                                                           \@headings,
 1110:                                                           $spacing,
 1111:                                                           \@studentInformation,
 1112:                                                           $r, $c);
 1113:     } elsif($GoToPage eq 'Analyze') {
 1114:         &Apache::lonproblemanalysis::BuildAnalyzePage($cacheDB, $students,
 1115:                                                       $courseID, $r);
 1116:     } elsif($GoToPage eq 'DoDiffGraph' || $GoToPage eq 'PercentWrongGraph') {
 1117:         my $courseDescription = $ENV{'course.'.$courseID.'.description'};
 1118:         $courseDescription =~ s/\ /"_"/eg;
 1119:         &Apache::lonproblemstatistics::BuildGraphicChart($GoToPage, $cacheDB,
 1120:                                                          $courseDescription,
 1121:                                                          $students, $courseID,
 1122:                                                          $r, $c);
 1123:     } elsif($GoToPage eq 'Class list') {
 1124:         &DisplayClasslist($r);
 1125: #        &BuildClasslist($cacheDB, $students, \@studentInformation,
 1126: #                        \@headings, $r);
 1127:     } elsif($GoToPage eq 'Correct-problems Plot') {
 1128: 	&Apache::lonpercentage::BuildPercentageGraph($cacheDB, $students,
 1129: 						     $courseID, $c, $r);
 1130:     }
 1131: 
 1132:     $r->print('</form>'."\n");
 1133:     $r->print("\n".'</body>'."\n".'</html>');
 1134:     $r->rflush();
 1135: 
 1136:     return OK;
 1137: }
 1138: 
 1139: # ================================================================ Main Handler
 1140: 
 1141: sub handler {
 1142:     my $r=shift;
 1143: 
 1144: #    $jr = $r;
 1145: 
 1146:     my $loaderror=&Apache::lonnet::overloaderror($r);
 1147:     if ($loaderror) { return $loaderror; }
 1148:     $loaderror=
 1149:        &Apache::lonnet::overloaderror($r,
 1150:          $ENV{'course.'.$ENV{'request.course.id'}.'.home'});
 1151:     if ($loaderror) { return $loaderror; }
 1152: 
 1153:     unless(&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) {
 1154:         $ENV{'user.error.msg'}=
 1155:         $r->uri.":vgr:0:0:Cannot view grades for complete course";
 1156:         return HTTP_NOT_ACCEPTABLE;
 1157:     }
 1158: 
 1159:     # Set document type for header only
 1160:     if($r->header_only) {
 1161:         if ($ENV{'browser.mathml'}) {
 1162:             $r->content_type('text/xml');
 1163:         } else {
 1164:             $r->content_type('text/html');
 1165:         }
 1166:         &Apache::loncommon::no_cache($r);
 1167:         $r->send_http_header;
 1168:         return OK;
 1169:     }
 1170: 
 1171:     unless($ENV{'request.course.fn'}) {
 1172: 	my $requrl=$r->uri;
 1173:         $ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized";
 1174:         return HTTP_NOT_ACCEPTABLE;
 1175:     }
 1176: 
 1177:     $r->content_type('text/html');
 1178:     $r->send_http_header;
 1179: 
 1180:     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
 1181:                                             ['sort']);
 1182: 
 1183:     &PrepareClasslist($r);
 1184: 
 1185:     &PrepareCourseData($r);
 1186: 
 1187:     &BuildStatistics($r);
 1188: 
 1189:     return OK;
 1190: }
 1191: 1;
 1192: 
 1193: =pod
 1194: 
 1195: =back
 1196: 
 1197: =cut
 1198: 
 1199: __END__
 1200: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>