File:  [LON-CAPA] / loncom / interface / lonstatistics.pm
Revision 1.105: download - view: text, annotated - select for diffs
Thu Jun 24 19:17:49 2004 UTC (20 years ago) by matthew
Branches: MAIN
CVS tags: HEAD
Bug 2405: CHRT select one student was dumb is now less so.  Added the
standard student selection dialogs (section and enrollment status) and an
'update display' submit button.  Added 'status' to the table of student
data.

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

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