File:  [LON-CAPA] / loncom / interface / lonstatistics.pm
Revision 1.85: download - view: text, annotated - select for diffs
Wed Oct 8 15:32:00 2003 UTC (20 years, 8 months ago) by matthew
Branches: MAIN
CVS tags: HEAD
Futher internationalization of lonstatistics.pm.
Added menu in lonstatistics to allow choice of statistical analyses or chart.
Added same menu text and some error messages to newphrases.txt to be localized.
Modified mydesk.tab to make [STAT] button take you to the statistics menu
and not the problem_statistics page.

    1: # The LearningOnline Network with CAPA
    2: #
    3: # $Id: lonstatistics.pm,v 1.85 2003/10/08 15:32:00 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 Apache::lonmysql;
   55: =over 4
   56: 
   57: =cut
   58: 
   59: package Apache::lonstatistics;
   60: 
   61: use strict;
   62: use Apache::Constants qw(:common :http);
   63: use vars qw(
   64:     @FullClasslist 
   65:     @Students
   66:     @Sections 
   67:     @SelectedSections
   68:     %StudentData
   69:     @StudentDataOrder
   70:     @SelectedStudentData
   71:     $top_map 
   72:     @Sequences 
   73:     @SelectedMaps
   74:     @Assessments);
   75: 
   76: use Apache::lonnet();
   77: use Apache::lonhomework;
   78: use Apache::loncommon;
   79: use Apache::loncoursedata;
   80: use Apache::lonhtmlcommon;
   81: use Apache::lonproblemanalysis();
   82: use Apache::lonproblemstatistics();
   83: use Apache::lonstudentassessment();
   84: use Apache::lonpercentage;
   85: use Apache::lonmysql;
   86: use Apache::lonlocal;
   87: use Time::HiRes;
   88: 
   89: #######################################################
   90: #######################################################
   91: 
   92: =pod
   93: 
   94: =item Package Variables
   95: 
   96: =item @FullClasslist The full classlist
   97: 
   98: =item @Students The students we are concerned with for this invocation
   99: 
  100: =item @Sections The sections available in this class
  101: 
  102: =item $curr_student The student currently being examined
  103: 
  104: =item $prev_student The student previous in the classlist
  105: 
  106: =item $next_student The student next in the classlist
  107: 
  108: =over
  109: 
  110: =cut 
  111: 
  112: #######################################################
  113: #######################################################
  114: #
  115: # Classlist variables
  116: #
  117: my $curr_student;
  118: my $prev_student;
  119: my $next_student;
  120: 
  121: #######################################################
  122: #######################################################
  123: 
  124: =pod
  125: 
  126: =item &clear_classlist_variables()
  127: 
  128: undef the following package variables:
  129: 
  130: =over
  131: 
  132: =item @FullClasslist
  133: 
  134: =item @Students
  135: 
  136: =item @Sections
  137: 
  138: =item @SelectedSections
  139: 
  140: =item %StudentData
  141: 
  142: =item @StudentDataOrder
  143: 
  144: =item @SelectedStudentData
  145: 
  146: =item $curr_student
  147: 
  148: =item $prev_student
  149: 
  150: =item $next_student
  151: 
  152: =back
  153: 
  154: =cut
  155: 
  156: #######################################################
  157: #######################################################
  158: sub clear_classlist_variables {
  159:     undef(@FullClasslist);
  160:     undef(@Students);
  161:     undef(@Sections);
  162:     undef(@SelectedSections);
  163:     undef(%StudentData);
  164:     undef(@SelectedStudentData);
  165:     undef($curr_student);
  166:     undef($prev_student);
  167:     undef($next_student);
  168: }
  169: 
  170: #######################################################
  171: #######################################################
  172: 
  173: =pod
  174: 
  175: =item &PrepareClasslist()
  176: 
  177: Build up the classlist information.  The classlist information is kept in
  178: the following package variables:
  179: 
  180: =over
  181: 
  182: =item @FullClasslist
  183: 
  184: =item @Students
  185: 
  186: =item @Sections
  187: 
  188: =item @SelectedSections
  189: 
  190: =item %StudentData
  191: 
  192: =item @SelectedStudentData
  193: 
  194: =item $curr_student
  195: 
  196: =item $prev_student
  197: 
  198: =item $next_student
  199: 
  200: =back
  201: 
  202: $curr_student, $prev_student, and $next_student may not be defined, depending
  203: upon the calling context.
  204: 
  205: =cut
  206: 
  207: #######################################################
  208: #######################################################
  209: sub PrepareClasslist {
  210:     my %Sections;
  211:     &clear_classlist_variables();
  212:     #
  213:     # Retrieve the classlist
  214:     my $cid  = $ENV{'request.course.id'};
  215:     my $cdom = $ENV{'course.'.$cid.'.domain'};
  216:     my $cnum = $ENV{'course.'.$cid.'.num'};
  217:     my ($classlist,$field_names) = &Apache::loncoursedata::get_classlist($cid,
  218:                                                                   $cdom,$cnum);
  219:     if (exists($ENV{'form.Section'})) {
  220:         if (ref($ENV{'form.Section'})) {
  221:             @SelectedSections = @{$ENV{'form.Section'}};
  222:         } elsif ($ENV{'form.Section'} !~ /^\s*$/) {
  223:             @SelectedSections = ($ENV{'form.Section'});
  224:         }
  225:     }
  226:     @SelectedSections = ('all') if (! @SelectedSections);
  227:     foreach (@SelectedSections) {
  228:         if ($_ eq 'all') {
  229:             @SelectedSections = ('all');
  230:         }
  231:     }
  232:     #
  233:     # Deal with instructors with restricted section access
  234:     if ($ENV{'request.course.sec'} !~ /^\s*$/) {
  235:         @SelectedSections = ($ENV{'request.course.sec'});
  236:     }
  237:     #
  238:     # Set up %StudentData
  239:     @StudentDataOrder = qw/fullname username domain id section status/;
  240:     foreach my $field (@StudentDataOrder) {
  241:         $StudentData{$field}->{'title'} = $field;
  242:         $StudentData{$field}->{'base_width'} = length($field);
  243:         $StudentData{$field}->{'width'} = 
  244:                                $StudentData{$field}->{'base_width'};
  245:     }
  246:     #
  247:     # get the status requested
  248:     my $requested_status = 'Active';
  249:     $requested_status = $ENV{'form.Status'} if (exists($ENV{'form.Status'}));
  250:     #
  251:     # Process the classlist
  252:     while (my ($student,$student_data) = each (%$classlist)) {
  253:         my $studenthash = ();
  254:         for (my $i=0; $i< scalar(@$field_names);$i++) {
  255:             my $field = $field_names->[$i];
  256:             # Store the data
  257:             $studenthash->{$field}=$student_data->[$i];
  258:             # Keep track of the width of the fields
  259:             next if (! exists($StudentData{$field}));
  260:             my $length = length($student_data->[$i]);
  261:             if ($StudentData{$field}->{'width'} < $length) {
  262:                 $StudentData{$field}->{'width'} = $length; 
  263:             }
  264:         }
  265:         push (@FullClasslist,$studenthash);
  266:         #
  267:         # Build up a list of sections
  268:         my $section = $studenthash->{'section'};
  269:         if (! defined($section) || $section =~/^\s*$/ || $section == -1) {
  270:             $studenthash->{'section'} = 'none';
  271:             $section = $studenthash->{'section'};
  272:         }
  273:         $Sections{$section}++;
  274:         #
  275:         # Only put in the list those students we are interested in
  276:         foreach my $sect (@SelectedSections) {
  277:             if ( (($sect eq 'all') || 
  278:                   ($section eq $sect)) &&
  279:                  (($studenthash->{'status'} eq $requested_status) || 
  280:                   ($requested_status eq 'Any')) 
  281:                  ){
  282:                 push (@Students,$studenthash);
  283:                 last;
  284:             }
  285:         }
  286:     }
  287:     #
  288:     # Put the consolidated section data in the right place
  289:     if ($ENV{'request.course.sec'} !~ /^\s*$/) {
  290:         @Sections = ($ENV{'request.course.sec'});
  291:     } else {
  292:         @Sections = sort {$a cmp $b} keys(%Sections);
  293:         unshift(@Sections,'all'); # Put 'all' at the front of the list
  294:     }
  295:     #
  296:     # Sort the Students
  297:     my $sortby = 'fullname';
  298:     $sortby = $ENV{'form.sort'} if (exists($ENV{'form.sort'}));
  299:     my @TmpStudents = sort { $a->{$sortby} cmp $b->{$sortby} ||
  300:                              $a->{'fullname'} cmp $b->{'fullname'} } @Students;
  301:     @Students = @TmpStudents;
  302:     # 
  303:     # Now deal with that current student thing....
  304:     $curr_student = undef;
  305:     if (exists($ENV{'form.SelectedStudent'})) {
  306:         my ($current_uname,$current_dom) = 
  307:             split(':',$ENV{'form.SelectedStudent'});
  308:         my $i;
  309:         for ($i = 0; $i<=$#Students; $i++) {
  310:             next if (($Students[$i]->{'username'} ne $current_uname) || 
  311:                      ($Students[$i]->{'domain'}   ne $current_dom));
  312:             $curr_student = $Students[$i];
  313:             last; # If we get here, we have our student.
  314:         }
  315:         if (defined($curr_student)) {
  316:             if ($i == 0) {
  317:                 $prev_student = undef;
  318:             } else {
  319:                 $prev_student = $Students[$i-1];
  320:             }
  321:             if ($i == $#Students) {
  322:                 $next_student = undef;
  323:             } else {
  324:                 $next_student = $Students[$i+1];
  325:             }
  326:         }
  327:     }
  328:     #
  329:     if (exists($ENV{'form.StudentData'})) {
  330:         if (ref($ENV{'form.StudentData'}) eq 'ARRAY') {
  331:             @SelectedStudentData = @{$ENV{'form.StudentData'}};
  332:         } else {
  333:             @SelectedStudentData = ($ENV{'form.StudentData'});
  334:         }
  335:     } else {
  336:         @SelectedStudentData = ('username');
  337:     }
  338:     foreach (@SelectedStudentData) {
  339:         if ($_ eq 'all') {
  340:             @SelectedStudentData = ('all');
  341:             last;
  342:         }
  343:     }
  344:     #
  345:     return;
  346: }
  347: 
  348: 
  349: #######################################################
  350: #######################################################
  351: 
  352: =pod
  353: 
  354: =item get_students
  355: 
  356: Returns a list of the selected students
  357: 
  358: =cut
  359: 
  360: #######################################################
  361: #######################################################
  362: sub get_students {
  363:     if (! @Students) {
  364:         &PrepareClasslist()
  365:     }
  366:     return @Students;
  367: }
  368: 
  369: #######################################################
  370: #######################################################
  371: 
  372: =pod
  373: 
  374: =item &current_student()
  375: 
  376: Returns a pointer to a hash containing data about the currently
  377: selected student.
  378: 
  379: =cut
  380: 
  381: #######################################################
  382: #######################################################
  383: sub current_student { 
  384:     return $curr_student;
  385: }
  386: 
  387: #######################################################
  388: #######################################################
  389: 
  390: =pod
  391: 
  392: =item &previous_student()
  393: 
  394: Returns a pointer to a hash containing data about the student prior
  395: in the list of students.  Or something.  
  396: 
  397: =cut
  398: 
  399: #######################################################
  400: #######################################################
  401: sub previous_student { 
  402:     return $prev_student;
  403: }
  404: 
  405: #######################################################
  406: #######################################################
  407: 
  408: =pod
  409: 
  410: =item &next_student()
  411: 
  412: Returns a pointer to a hash containing data about the next student
  413: to be viewed.
  414: 
  415: =cut
  416: 
  417: #######################################################
  418: #######################################################
  419: sub next_student { 
  420:     return $next_student;
  421: }
  422: 
  423: #######################################################
  424: #######################################################
  425: 
  426: =pod
  427: 
  428: =item &clear_sequence_variables()
  429: 
  430: =cut
  431: 
  432: #######################################################
  433: #######################################################
  434: sub clear_sequence_variables {
  435:     undef($top_map);
  436:     undef(@Sequences);
  437:     undef(@Assessments);
  438: }
  439: 
  440: #######################################################
  441: #######################################################
  442: 
  443: =pod
  444: 
  445: =item &SetSelectedMaps($elementname)
  446: 
  447: Sets the @SelectedMaps array from $ENV{'form.'.$elementname};
  448: 
  449: =cut
  450: 
  451: #######################################################
  452: #######################################################
  453: sub SetSelectedMaps {
  454:     my $elementname = shift;
  455:     if (exists($ENV{'form.'.$elementname})) {
  456:         if (ref($ENV{'form.'.$elementname})) {
  457:             @SelectedMaps = @{$ENV{'form.'.$elementname}};
  458:         } else {
  459:             @SelectedMaps = ($ENV{'form.'.$elementname});
  460:         }
  461:     } else {
  462:         @SelectedMaps = ('all');
  463:     }
  464: }
  465: 
  466: 
  467: #######################################################
  468: #######################################################
  469: 
  470: =pod
  471: 
  472: =item &Sequences_with_Assess()
  473: 
  474: Returns an array containing the subset of @Sequences which contain
  475: assessments.
  476: 
  477: =cut
  478: 
  479: #######################################################
  480: #######################################################
  481: sub Sequences_with_Assess {
  482:     my @Sequences_to_Show;
  483:     foreach my $map_symb (@SelectedMaps) {
  484:         foreach my $sequence (@Sequences) {
  485:             next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all');
  486:             next if ($sequence->{'num_assess'} < 1);
  487:             push (@Sequences_to_Show,$sequence);
  488:         }
  489:     }
  490:     return @Sequences_to_Show;
  491: }
  492: 
  493: #######################################################
  494: #######################################################
  495: 
  496: =pod
  497: 
  498: =item &PrepareCourseData($r)
  499: 
  500: =cut
  501: 
  502: #######################################################
  503: #######################################################
  504: sub PrepareCourseData {
  505:     my ($r) = @_;
  506:     &clear_sequence_variables();
  507:     my ($top,$sequences,$assessments) = 
  508:         &Apache::loncoursedata::get_sequence_assessment_data();
  509:     if (! defined($top) || ! ref($top)) {
  510:         # There has been an error, better report it
  511:         &Apache::lonnet::logthis('top is undefined');
  512:         return;
  513:     }
  514:     $top_map = $top if (ref($top));
  515:     @Sequences = @{$sequences} if (ref($sequences) eq 'ARRAY');
  516:     @Assessments = @{$assessments} if (ref($assessments) eq 'ARRAY');
  517:     #
  518:     # Compute column widths
  519:     foreach my $seq (@Sequences) {
  520:         my $name_length = length($seq->{'title'});
  521:         my $num_parts = $seq->{'num_assess_parts'};
  522:         #
  523:         # Use 3 digits for each the sum and total, which means 7 total...
  524:         my $num_col = $num_parts+7;
  525:         if ($num_col < $name_length) {
  526:             $num_col = $name_length;
  527:         }
  528:         $seq->{'base_width'} = $name_length;
  529:         $seq->{'width'} = $num_col;
  530:     }
  531:     return;
  532: }
  533: 
  534: #######################################################
  535: #######################################################
  536: 
  537: =pod
  538: 
  539: =item &log_sequence($sequence,$recursive,$padding)
  540: 
  541: Write data about the sequence to a logfile.  If $recursive is not
  542: undef the data is written recursively.  $padding is used for recursive
  543: calls.
  544: 
  545: =cut
  546: 
  547: #######################################################
  548: #######################################################
  549: sub log_sequence {
  550:     my ($seq,$recursive,$padding) = @_;
  551:     $padding = '' if (! defined($padding));
  552:     if (ref($seq) ne 'HASH') {
  553:         &Apache::lonnet::logthis('log_sequence passed bad sequnce');
  554:         return;
  555:     }
  556:     &Apache::lonnet::logthis($padding.'sequence '.$seq->{'title'});
  557:     while (my($key,$value) = each(%$seq)) {
  558:         next if ($key eq 'contents');
  559:         if (ref($value) eq 'ARRAY') {
  560:             for (my $i=0;$i< scalar(@$value);$i++) {
  561:                 &Apache::lonnet::logthis($padding.$key.'['.$i.']='.
  562:                                          $value->[$i]);
  563:             }
  564:         } else {
  565:             &Apache::lonnet::logthis($padding.$key.'='.$value);
  566:         }
  567:     }
  568:     if (defined($recursive)) {
  569:         &Apache::lonnet::logthis($padding.'-'x20);
  570:         &Apache::lonnet::logthis($padding.'contains:');
  571:         foreach my $item (@{$seq->{'contents'}}) {
  572:             if ($item->{'type'} eq 'container') {
  573:                 &log_sequence($item,$recursive,$padding.'    ');
  574:             } else {
  575:                 &Apache::lonnet::logthis($padding.'title = '.$item->{'title'});
  576:                 while (my($key,$value) = each(%$item)) {
  577:                     next if ($key eq 'title');
  578:                     if (ref($value) eq 'ARRAY') {
  579:                         for (my $i=0;$i< scalar(@$value);$i++) {
  580:                             &Apache::lonnet::logthis($padding.$key.'['.$i.']='.
  581:                                                      $value->[$i]);
  582:                         }
  583:                     } else {
  584:                         &Apache::lonnet::logthis($padding.$key.'='.$value);
  585:                     }
  586:                 }
  587:             }
  588:         }
  589:         &Apache::lonnet::logthis($padding.'end contents of '.$seq->{'title'});
  590:         &Apache::lonnet::logthis($padding.'-'x20);
  591:     }
  592:     return;
  593: }
  594: 
  595: ##############################################
  596: ##############################################
  597: 
  598: =pod 
  599: 
  600: =item &StudentDataSelect($elementname,$status,$numvisible,$selected)
  601: 
  602: Returns html for a selection box allowing the user to choose one (or more) 
  603: of the fields of student data available (fullname, username, id, section, etc)
  604: 
  605: =over 4
  606: 
  607: =item $elementname The name of the HTML form element
  608: 
  609: =item $status 'multiple' or 'single' selection box
  610: 
  611: =item $numvisible The number of options to be visible
  612: 
  613: =back
  614: 
  615: =cut
  616: 
  617: ##############################################
  618: ##############################################
  619: sub StudentDataSelect {
  620:     my ($elementname,$status,$numvisible)=@_;
  621:     if ($numvisible < 1) {
  622:         return;
  623:     }
  624:     #
  625:     # Build the form element
  626:     my $Str = "\n";
  627:     $Str .= '<select name="'.$elementname.'" ';
  628:     if ($status ne 'single') {
  629:         $Str .= 'multiple="true" ';
  630:     }
  631:     $Str .= 'size="'.$numvisible.'" >'."\n";
  632:     #
  633:     # Deal with 'all'
  634:     $Str .= '    <option value="all" ';
  635:     foreach (@SelectedStudentData) {
  636:         if ($_ eq 'all') {
  637:             $Str .= 'selected ';
  638:             last;
  639:         }
  640:     }
  641:     $Str .= ">all</option>\n";
  642:     #
  643:     # Loop through the student data fields
  644:     foreach my $item (@StudentDataOrder) {
  645:         $Str .= '    <option value="'.$item.'" ';
  646:         foreach (@SelectedStudentData) {
  647:             if ($item eq $_ ) {
  648:                 $Str .= 'selected ';
  649:                 last;
  650:             }
  651:         }
  652:         $Str .= '>'.$item."</option>\n";
  653:     }
  654:     $Str .= "</select>\n";
  655:     return $Str;
  656: }
  657: 
  658: ##############################################
  659: ##############################################
  660: 
  661: =pod 
  662: 
  663: =item &MapSelect($elementname,$status,$numvisible,$restriction) 
  664: 
  665: Returns html for a selection box allowing the user to choose one (or more) 
  666: of the sequences in the course.  The values of the sequences are the symbs.
  667: If the top sequence is selected, the value 'top' will result.
  668: 
  669: =over 4
  670: 
  671: =item $elementname The name of the HTML form element
  672: 
  673: =item $status 'multiple' or 'single' selection box
  674: 
  675: =item $numvisible The number of options to be visible
  676: 
  677: =item $restriction Code reference to subroutine which returns true or 
  678: false.  The code must expect a reference to a sequence data structure.
  679: 
  680: =back
  681: 
  682: =cut
  683: 
  684: ##############################################
  685: ##############################################
  686: sub MapSelect {
  687:     my ($elementname,$status,$numvisible,$restriction)=@_;
  688:     if ($numvisible < 1) {
  689:         return;
  690:     }
  691:     #
  692:     # Set up array of selected items
  693:     &SetSelectedMaps($elementname);
  694:     #
  695:     # Set up the restriction call
  696:     if (! defined($restriction)) {
  697:         $restriction = sub { 1; };
  698:     }
  699:     #
  700:     # Build the form element
  701:     my $Str = "\n";
  702:     $Str .= '<select name="'.$elementname.'" ';
  703:     if ($status ne 'single') {
  704:         $Str .= 'multiple="true" ';
  705:     }
  706:     $Str .= 'size="'.$numvisible.'" >'."\n";
  707:     #
  708:     # Deal with 'all'
  709:     foreach (@SelectedMaps) {
  710:         if ($_ eq 'all') {
  711:             @SelectedMaps = ('all');
  712:             last;
  713:         }
  714:     }
  715:     #
  716:     # Put in option for 'all'
  717:     $Str .= '    <option value="all" ';
  718:     foreach (@SelectedMaps) {
  719:         if ($_ eq 'all') {
  720:             $Str .= 'selected ';
  721:             last;
  722:         }
  723:     }
  724:     $Str .= ">all</option>\n";
  725:     #
  726:     # Loop through the sequences
  727:     foreach my $seq (@Sequences) {
  728:         next if (! $restriction->($seq));
  729:         $Str .= '    <option value="'.$seq->{'symb'}.'" ';
  730:         foreach (@SelectedMaps) {
  731:             if ($seq->{'symb'} eq $_) {
  732:                 $Str .= 'selected ';
  733:                 last;
  734:             }
  735:         }
  736:         $Str .= '>'.$seq->{'title'}."</option>\n";
  737:     }
  738:     $Str .= "</select>\n";
  739:     return $Str;
  740: }
  741: 
  742: ##############################################
  743: ##############################################
  744: 
  745: =pod 
  746: 
  747: =item &SectionSelect($elementname,$status,$numvisible) 
  748: 
  749: Returns html for a selection box allowing the user to choose one (or more) 
  750: of the sections in the course.  
  751: 
  752: Uses the package variables @Sections and @SelectedSections
  753: =over 4
  754: 
  755: =item $elementname The name of the HTML form element
  756: 
  757: =item $status 'multiple' or 'single' selection box
  758: 
  759: =item $numvisible The number of options to be visible
  760: 
  761: =back
  762: 
  763: =cut
  764: 
  765: ##############################################
  766: ##############################################
  767: sub SectionSelect {
  768:     my ($elementname,$status,$numvisible)=@_;
  769:     if ($numvisible < 1) {
  770:         return;
  771:     }
  772:     #
  773:     # Make sure we have the data we need to continue
  774:     if (! @Sections) {
  775:         &PrepareClasslist()
  776:     }
  777:     #
  778:     # Build the form element
  779:     my $Str = "\n";
  780:     $Str .= '<select name="'.$elementname.'" ';
  781:     if ($status ne 'single') {
  782:         $Str .= 'multiple="true" ';
  783:     }
  784:     $Str .= 'size="'.$numvisible.'" >'."\n";
  785:     #
  786:     # Loop through the sequences
  787:     foreach my $s (@Sections) {
  788:         $Str .= '    <option value="'.$s.'" ';
  789:         foreach (@SelectedSections) {
  790:             if ($s eq $_) {
  791:                 $Str .= 'selected ';
  792:                 last;
  793:             }
  794:         }
  795:         $Str .= '>'.$s."</option>\n";
  796:     }
  797:     $Str .= "</select>\n";
  798:     return $Str;
  799: }
  800: 
  801: #######################################################
  802: #######################################################
  803: 
  804: =pod
  805: 
  806: =item &CreateAndParseOutputSelector()
  807: 
  808: Construct a selection list of options for output and parse output selections.
  809: 
  810: =cut
  811: 
  812: #######################################################
  813: #######################################################
  814: sub OutputDescriptions {
  815:     my (@OutputOptions) = @_;
  816:     my $Str = '';
  817:     $Str .= "<h2>Output Modes</h2>\n";
  818:     $Str .= "<dl>\n";
  819:     foreach my $outputmode (@OutputOptions) {
  820: 	$Str .="    <dt>".$outputmode->{'name'}."</dt>\n";
  821: 	$Str .="        <dd>".$outputmode->{'description'}."</dd>\n";
  822:     }
  823:     $Str .= "</dl>\n";
  824:     return $Str;
  825: }
  826: 
  827: sub CreateAndParseOutputSelector {
  828:     my ($elementname,$default,@OutputOptions) = @_;
  829:     my $output_mode;
  830:     my $show;
  831:     my $Str = '';
  832:     #
  833:     # Format for output options is 'mode, restrictions';
  834:     my $selected = $default;
  835:     if (exists($ENV{'form.'.$elementname})) {
  836:         if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
  837:             $selected = $ENV{'form.'.$elementname}->[0];
  838:         } else {
  839:             $selected = $ENV{'form.'.$elementname};
  840:         }
  841:     }
  842:     #
  843:     # Set package variables describing output mode
  844:     $output_mode = 'html';
  845:     $show        = 'all';
  846:     foreach my $option (@OutputOptions) {
  847:         next if ($option->{'value'} ne $selected);
  848:         $output_mode = $option->{'mode'};
  849:         $show        = $option->{'show'};
  850:     }
  851:     #
  852:     # Build the form element
  853:     $Str = qq/<select size="5" name="$elementname">/;
  854:     foreach my $option (@OutputOptions) {
  855:         if (exists($option->{'special'}) && 
  856:             $option->{'special'} =~ /do not show/) {
  857:             next;
  858:         }
  859:         $Str .= "\n".'    <option value="'.$option->{'value'}.'"';
  860:         $Str .= " selected " if ($option->{'value'} eq $selected);
  861:         $Str .= ">".&mt($option->{'name'})."<\/option>";
  862:     }
  863:     $Str .= "\n</select>";
  864:     return ($Str,$output_mode,$show);
  865: }
  866: 
  867: ###############################################
  868: ###############################################
  869: 
  870: =pod 
  871: 
  872: =item &Gather_Student_Data()
  873: 
  874: Ensures all student data is up to date.
  875: 
  876: =cut
  877: 
  878: ###############################################
  879: ###############################################
  880: sub Gather_Student_Data {
  881:     my ($r) = @_;
  882:     my $c = $r->connection();
  883:     #
  884:     &Apache::loncoursedata::clear_internal_caches();
  885:     #
  886:     my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
  887:     #
  888:     my @Students = @Apache::lonstatistics::Students;
  889:     #
  890:     # Open the progress window
  891:     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
  892:         ($r,'Statistics Compilation Status',
  893:          'Statistics Compilation Progress', scalar(@Students));
  894:     #
  895:     while (my $student = shift @Students) {
  896:         return if ($c->aborted());
  897:         my ($status,undef) = &Apache::loncoursedata::ensure_current_data
  898:             ($student->{'username'},$student->{'domain'},
  899:              $ENV{'request.course.id'});
  900:         &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
  901:                                                  'last student');
  902:     }
  903:     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
  904:     $r->rflush();
  905: }
  906: 
  907: ###############################################
  908: ###############################################
  909: 
  910: =pod 
  911: 
  912: =item &Gather_Full_Student_Data()
  913: 
  914: Ensures all student data is up to date.
  915: 
  916: =cut
  917: 
  918: ###############################################
  919: ###############################################
  920: sub Gather_Full_Student_Data {
  921:     my ($r) = @_;
  922:     my $c = $r->connection();
  923:     #
  924:     &Apache::loncoursedata::clear_internal_caches();
  925:     #
  926:     my @Students = @Apache::lonstatistics::Students;
  927:     #
  928:     # Open the progress window
  929:     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
  930:         ($r,&mt('Student Data Compilation Status'),
  931:          &mt('Student Data Compilation Progress'), scalar(@Students));
  932:     #
  933:     while (my $student = shift @Students) {
  934:         return if ($c->aborted());
  935:         my ($status,undef) = &Apache::loncoursedata::ensure_current_full_data
  936:             ($student->{'username'},$student->{'domain'},
  937:              $ENV{'request.course.id'});
  938:         &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
  939:                                                  &mt('last student'));
  940:     }
  941:     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
  942:     $r->rflush();
  943: }
  944: 
  945: ##################################################
  946: ##################################################
  947: sub DisplayClasslist {
  948:     my ($r)=@_;
  949:     #
  950:     my @Fields = ('fullname','username','domain','id','section');
  951:     #
  952:     my $Str='';
  953:     if (! @Students) {
  954:         if ($SelectedSections[0] eq 'all') { 
  955:             if (lc($ENV{'form.Status'}) eq 'any') {
  956:                 $Str .= '<h2>There are no students in the course.</h2>';
  957:             } elsif (lc($ENV{'form.Status'}) eq 'active') {
  958:                 $Str .= '<h2>There are no currently enrolled students in '.
  959:                     'the course.</h2>';
  960:             } elsif (lc($ENV{'form.Status'}) eq 'expired') {
  961:                 $Str .= '<h2>There are no previously enrolled '.
  962:                     'students in the course.</h2>';
  963:             }
  964:         } else { 
  965:             my $sections;
  966:             if (@SelectedSections == 1) {
  967:                 $sections = 'section '.$SelectedSections[0];
  968:             } elsif (@SelectedSections > 2) {
  969:                 $sections = 'sections '.join(', ',@SelectedSections);
  970:                 $sections =~ s/, ([^,])*$/, and $1/;
  971:             } else {
  972:                 $sections = 'sections '.join(' and ',@SelectedSections);
  973:             }
  974:             if (lc($ENV{'form.Status'}) eq 'any') {
  975:                 $Str .= '<h2>There are no students in '.$sections.'.</h2>';
  976:             } elsif (lc($ENV{'form.Status'}) eq 'active') {
  977:                 $Str .= '<h2>There are no currently enrolled students '.
  978:                     'in '.$sections.'.</h2>';
  979:             } elsif (lc($ENV{'form.Status'}) eq 'expired') {
  980:                 $Str .= '<h2>There are no previously enrolled students '.
  981:                     'in '.$sections.'.</h2>';
  982:             }
  983:         }
  984:         $Str.= '<a href="/adm/statistics?reportSelected=student_assessment">'.
  985:             'Return to the chart.</a>';
  986:         $r->print($Str);
  987:         $r->rflush();
  988:         return;
  989:     }
  990: 
  991:     # "Click" is asinine but it is probably not my place to change the world.
  992:     $Str .= '<h2>Click on a students name or username to view their chart</h2>';
  993:     $Str .= '<table border="0"><tr><td bgcolor="#777777">'."\n";
  994:     $Str .= '<table border="0" cellpadding="3"><tr bgcolor="#e6ffff">'."\n";
  995:     foreach my $field (@Fields) {
  996:         $Str .= '<th><a href="/adm/statistics?reportSelected=classlist&sort='.$field.'">'.$field.
  997:             '</a></th>';
  998:     }
  999:     $Str .= '</tr>'."\n";
 1000:     #
 1001:     my $alternate = 0;
 1002:     foreach my $student (@Students) { # @Students is a package variable
 1003:         my $sname = $student->{'username'}.':'.$student->{'domain'};
 1004:         if($alternate) {
 1005:             $Str .= '<tr bgcolor="#ffffe6">';
 1006:         } else {
 1007:             $Str .= '<tr bgcolor="#ffffc6">';
 1008:         }
 1009:         $alternate = ($alternate + 1) % 2;
 1010:         #
 1011:         foreach my $field (@Fields) {
 1012:             $Str .= '<td>';
 1013:             if ($field eq 'fullname' || $field eq 'username') {
 1014:                 $Str .= '<a href="/adm/statistics?reportSelected=';
 1015:                 $Str .= &Apache::lonnet::escape('student_assessment');
 1016:                 $Str .= '&sort='.&Apache::lonnet::escape($ENV{'form.sort'});
 1017:                 $Str .= '&SelectedStudent=';
 1018:                 $Str .= &Apache::lonnet::escape($sname).'">';
 1019:                 $Str .= $student->{$field}.'&nbsp';
 1020:                 $Str .= '</a>';
 1021:             } else {
 1022:                 $Str .= $student->{$field};
 1023:             }
 1024:             $Str .= '</td>';
 1025:         }
 1026:         $Str .= "</tr>\n";
 1027:     }
 1028:     $Str .= '</table></td></tr></table>'."\n";
 1029:     #
 1030:     $r->print($Str);
 1031:     $r->rflush();
 1032:     #
 1033:     return;
 1034: }
 1035: 
 1036: ##############################################
 1037: ##############################################
 1038: sub CreateMainMenu {
 1039:     #
 1040:     # Define menu data
 1041:     my @reports = ({ internal_name => 'problem_statistics',
 1042:                      name => &mt('Overall Problem Statistics'),
 1043:                      short_description => 
 1044:     &mt('Student performance statistics on all problems.'),
 1045:                  },
 1046:                    { internal_name => 'problem_analysis',
 1047:                      name => &mt('Detailed Problem Analysis'),
 1048:                      short_description => 
 1049:     &mt('Detailed statistics and graphs of student performance on problems.'),
 1050:                  },
 1051:                    { internal_name => 'student_assessment',
 1052:                      name => &mt('Problem Status Chart'),
 1053:                      short_description => 
 1054:     &mt('Brief view of each students performance in course.'),
 1055:                  },
 1056:                    # 'percentage'  => 'Correct-problems Plot',
 1057:                    # 'activitylog' => 'Activity Log',
 1058:                    );
 1059: 
 1060:     #
 1061:     # Create the menu
 1062:     my $Str;
 1063:     $Str =  "<h2>".&mt('Statistics and Problem Analysis')."</h2>\n";
 1064:     $Str .= &mt('Please select a report to generate.');
 1065:     $Str .= "<dl>\n";
 1066:     foreach my $reportdata (@reports) {
 1067:         $Str .='    <dt><a href="/adm/statistics?reportSelected='.
 1068:             $reportdata->{'internal_name'}.'" >'.
 1069:             $reportdata->{'name'}."</a></dt>\n";
 1070:         $Str .= '        <dd>'.$reportdata->{'short_description'}.
 1071:             "</dd>\n";
 1072:     }
 1073:     $Str .="</dl>\n";
 1074:     #
 1075:     return $Str;
 1076: }
 1077: 
 1078: ##############################################
 1079: ##############################################
 1080: sub handler {
 1081:     my $r=shift;
 1082:     my $c = $r->connection();
 1083:     #
 1084:     # Check for overloading
 1085:     my $loaderror=&Apache::lonnet::overloaderror($r);
 1086:     if ($loaderror) { return $loaderror; }
 1087:     $loaderror=
 1088:        &Apache::lonnet::overloaderror($r,
 1089:          $ENV{'course.'.$ENV{'request.course.id'}.'.home'});
 1090:     if ($loaderror) { return $loaderror; }
 1091:     #
 1092:     # Check for access
 1093:     if (! &Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) {
 1094:         $ENV{'user.error.msg'}=
 1095:             $r->uri.":vgr:0:0:Cannot view grades for complete course";
 1096:         if (! &Apache::lonnet::allowed('vgr',
 1097:                       $ENV{'request.course.id'}.'/'.$ENV{'request.course.sec'})) {
 1098:             $ENV{'user.error.msg'}=
 1099:                 $r->uri.":vgr:0:0:Cannot view grades with given role";
 1100:             return HTTP_NOT_ACCEPTABLE;
 1101:         }
 1102:     }
 1103:     #
 1104:     # Set document type for header only
 1105:     if($r->header_only) {
 1106:         if ($ENV{'browser.mathml'}) {
 1107:             $r->content_type('text/xml');
 1108:         } else {
 1109:             $r->content_type('text/html');
 1110:         }
 1111:         &Apache::loncommon::no_cache($r);
 1112:         $r->send_http_header;
 1113:         return OK;
 1114:     }
 1115:     #
 1116:     # Send the header
 1117:     $r->content_type('text/html');
 1118:     $r->send_http_header;
 1119:     #
 1120:     # Extract form elements from query string
 1121:     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
 1122:                                             ['sort','reportSelected',
 1123:                                              'SelectedStudent']);
 1124:     #
 1125:     # Give the LON-CAPA page header
 1126:     $r->print(&Apache::lonhtmlcommon::Title('Course Statistics and Charts'));
 1127:     $r->rflush();
 1128:     # 
 1129:     # Either print out a menu for them or send them to a report
 1130:     if (! exists($ENV{'form.reportSelected'}) || 
 1131:         $ENV{'form.reportSelected'} eq '') {
 1132:         $r->print(&CreateMainMenu());
 1133:     } else {
 1134:     #
 1135:         if (! &Apache::lonmysql::verify_sql_connection()) {
 1136:             my $serveradmin = $r->dir_config('lonAdmEMail');
 1137:             $r->print('<h2><font color="Red">'.
 1138:                       &mt('Unable to connect to database!').
 1139:                       '</font></h2>');
 1140:             $r->print('<p>'.
 1141:                       &mt('Please notify the server administrator ').
 1142:                       '<b>'.$serveradmin.'</b></p>');
 1143:             $r->print('<p>'.
 1144:                       &mt('Course Statistics and Charts cannot be '.
 1145:                           'retrieved until the database is restarted.  '.
 1146:                           'Your data is intact but cannot be displayed '.
 1147:                           'at this time.').'</p>');
 1148:             $r->print('</body></html>');
 1149:             return;
 1150:         }
 1151:         #
 1152:         # Clean out the caches
 1153:         if (exists($ENV{'form.ClearCache'})) {
 1154:             &Apache::loncoursedata::delete_caches($ENV{'requres.course.id'});
 1155:         }
 1156:         #
 1157:         # Set up the statistics and chart environment
 1158:         &PrepareClasslist();
 1159:         &PrepareCourseData($r);
 1160:         #
 1161:         # Begin form output
 1162:         $r->print('<form name="Statistics" ');
 1163:         $r->print('method="post" action="/adm/statistics">');
 1164:         $r->rflush();
 1165:         #
 1166:         my $GoToPage = $ENV{'form.reportSelected'};
 1167:         $r->print('<input type="hidden" name="reportSelected" value="'.
 1168:                   $GoToPage.'">');
 1169:         if($GoToPage eq 'activitylog') {
 1170: #        &Apache::lonproblemstatistics::Activity();
 1171:         } elsif($GoToPage eq 'problem_statistics') {
 1172:             &Apache::lonproblemstatistics::BuildProblemStatisticsPage($r,$c);
 1173:         } elsif($GoToPage eq 'problem_analysis') {
 1174:             &Apache::lonproblemanalysis::BuildProblemAnalysisPage($r,$c);
 1175:         } elsif($GoToPage eq 'student_assessment') {
 1176:             &Apache::lonstudentassessment::BuildStudentAssessmentPage($r,$c);
 1177:         } elsif($GoToPage eq 'DoDiffGraph' || $GoToPage eq 'PercentWrongGraph') {
 1178: #        &Apache::lonproblemstatistics::BuildGraphicChart($r,$c);
 1179:         } elsif($GoToPage eq 'Correct-problems Plot') {
 1180:             #	&Apache::lonpercentage::BuildPercentageGraph($r,$c);
 1181:         }
 1182:         #
 1183:         $r->print("</form>\n");
 1184:     }
 1185:     $r->print("</body>\n</html>\n");
 1186:     $r->rflush();
 1187:     #
 1188:     return OK;
 1189: }
 1190: 
 1191: 1;
 1192: 
 1193: #######################################################
 1194: #######################################################
 1195: 
 1196: =pod
 1197: 
 1198: =back
 1199: 
 1200: =cut
 1201: 
 1202: #######################################################
 1203: #######################################################
 1204: 
 1205: __END__
 1206: 

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