File:  [LON-CAPA] / loncom / interface / statistics / lonproblemstatistics.pm
Revision 1.47: download - view: text, annotated - select for diffs
Thu Mar 27 19:26:33 2003 UTC (21 years, 4 months ago) by matthew
Branches: MAIN
CVS tags: HEAD
Added beginnings of POD.
Added prompt for the user to hit 'update display' on the first run.
Removed problem part supression from html grouped by sequence display
   because it just did not work right.
Added default output mode choice.
'Mod' is now displayed as an integer (which it is) instead of %5.2f.

    1: # The LearningOnline Network with CAPA
    2: #
    3: # $Id: lonproblemstatistics.pm,v 1.47 2003/03/27 19:26:33 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: 
   32: =pod
   33: 
   34: =head1 NAME
   35: 
   36: lonproblemstatistics
   37: 
   38: =head1 SYNOPSIS
   39: 
   40: Routines to present problem statistics to instructors via tables,
   41: Excel files, and plots.
   42: 
   43: =over 4
   44: 
   45: =cut
   46: 
   47: ###############################################
   48: ###############################################
   49: 
   50: package Apache::lonproblemstatistics;
   51: 
   52: use strict;
   53: use Apache::lonnet();
   54: use Apache::lonhtmlcommon;
   55: use Apache::loncoursedata;
   56: use Apache::lonstatistics;
   57: use Spreadsheet::WriteExcel;
   58: 
   59: ###############################################
   60: ###############################################
   61: 
   62: =pod 
   63: 
   64: =item &CreateInterface()
   65: 
   66: Create the main intereface for the statistics page.  Allows the user to
   67: select sections, maps, and output.
   68: 
   69: =cut
   70: 
   71: ###############################################
   72: ###############################################
   73: sub CreateInterface {
   74:     my $Str = '';
   75:     $Str .= '<table cellspacing="5">'."\n";
   76:     $Str .= '<tr>';
   77:     $Str .= '<td align="center"><b>Sections</b></td>';
   78:     $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
   79:     $Str .= '<td align="center"><b>Output</b></td>';
   80:     $Str .= '</tr>'."\n";
   81:     #
   82:     $Str .= '<tr><td align="center">'."\n";
   83:     $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
   84:     $Str .= '</td><td align="center">';
   85:     #
   86:     my $only_seq_with_assessments = sub { 
   87:         my $s=shift;
   88:         if ($s->{'num_assess'} < 1) { 
   89:             return 0;
   90:         } else { 
   91:             return 1;
   92:         }
   93:     };
   94:     $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
   95:                                               $only_seq_with_assessments);
   96:     $Str .= '</td><td>'."\n";
   97:     $Str .= &CreateAndParseOutputSelector();
   98:     $Str .= '</td></tr>'."\n";
   99:     $Str .= '</table>'."\n";
  100:     return $Str;
  101: }
  102: 
  103: #######################################################
  104: #######################################################
  105: 
  106: =pod
  107: 
  108: =item &CreateAndParseOutputSelector()
  109: 
  110: Construct a selection list of options for output and parse output selections.
  111: The current output selected is indicated by the values of the two package
  112: variables $output_mode and $show.  @OutputOptions holds the descriptions of
  113: the output options and the values for $output_mode and $show.
  114: 
  115:  Based on code from lonstudentassessment.pm.
  116: 
  117: =cut
  118: 
  119: #######################################################
  120: #######################################################
  121: my $output_mode;
  122: my $show;
  123: 
  124: my @OutputOptions = 
  125:     (
  126:      { name  => 'problem statistics grouped by sequence',
  127:        value => 'HTML problem statistics grouped',
  128:        description => 'Output statistics for the problem parts.',
  129:        mode => 'html',
  130:        show => 'grouped',
  131:      },
  132:      { name  => 'problem statistics ungrouped',
  133:        value => 'HTML problem statistics ungrouped',
  134:        description => 'Output statistics for the problem parts.',
  135:        mode => 'html',
  136:        show => 'ungrouped',
  137:      },
  138:      { name  => 'problem statistics, Excel',
  139:        value => 'Excel problem statistics',
  140:        description => 'Output statistics for the problem parts '.
  141:            'in an Excel workbook',
  142:        mode => 'excel',
  143:        show => 'all',
  144:      },
  145:      { name  => 'Degree of Difficulty Plot',
  146:        value => 'plot deg diff',
  147:        description => 'Generate a plot of the degree of difficulty of each '.
  148:            'problem part.',
  149:        mode => 'plot',
  150:        show => 'deg of diff',
  151:      },
  152:      { name  => 'Percent Wrong Plot',
  153:        value => 'plot per wrong',
  154:        description => 'Generate a plot showing the percent of students who '.
  155:            'were unable to complete each problem part',
  156:        mode => 'plot',
  157:        show => 'per wrong',
  158:      },
  159:      );
  160: 
  161: sub OutputDescriptions {
  162:     my $Str = '';
  163:     $Str .= "<h2>Output Modes</h2>\n";
  164:     $Str .= "<dl>\n";
  165:     foreach my $outputmode (@OutputOptions) {
  166: 	$Str .="    <dt>".$outputmode->{'name'}."</dt>\n";
  167: 	$Str .="        <dd>".$outputmode->{'description'}."</dd>\n";
  168:     }
  169:     $Str .= "</dl>\n";
  170:     return $Str;
  171: }
  172: 
  173: sub CreateAndParseOutputSelector {
  174:     my $Str = '';
  175:     my $elementname = 'statsoutputmode';
  176:     #
  177:     # Format for output options is 'mode, restrictions';
  178:     my $selected = 'HTML problem statistics grouped';
  179:     if (exists($ENV{'form.'.$elementname})) {
  180:         if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
  181:             $selected = $ENV{'form.'.$elementname}->[0];
  182:         } else {
  183:             $selected = $ENV{'form.'.$elementname};
  184:         }
  185:     }
  186:     #
  187:     # Set package variables describing output mode
  188:     $output_mode = 'html';
  189:     $show        = 'all';
  190:     foreach my $option (@OutputOptions) {
  191:         next if ($option->{'value'} ne $selected);
  192:         $output_mode = $option->{'mode'};
  193:         $show        = $option->{'show'};
  194:     }
  195:     #
  196:     # Build the form element
  197:     $Str = qq/<select size="5" name="$elementname">/;
  198:     foreach my $option (@OutputOptions) {
  199:         $Str .= "\n".'    <option value="'.$option->{'value'}.'"';
  200:         $Str .= " selected " if ($option->{'value'} eq $selected);
  201:         $Str .= ">".$option->{'name'}."<\/option>";
  202:     }
  203:     $Str .= "\n</select>";
  204:     return $Str;
  205: }
  206: 
  207: ###############################################
  208: ###############################################
  209: 
  210: =pod 
  211: 
  212: =item &Gather_Student_Data()
  213: 
  214: Ensures all student data is up to date.
  215: 
  216: =cut
  217: 
  218: ###############################################
  219: ###############################################
  220: sub Gather_Student_Data {
  221:     my ($r) = @_;
  222:     my $c = $r->connection();
  223:     #
  224:     my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
  225:     #
  226:     my @Students = @Apache::lonstatistics::Students;
  227:     #
  228:     # Open the progress window
  229:     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
  230:         ($r,'Statistics Compilation Status',
  231:          'Statistics Compilation Progress', scalar(@Students));
  232:     #
  233:     while (my $student = shift @Students) {
  234:         return if ($c->aborted());
  235:         my ($status,undef) = &Apache::loncoursedata::ensure_current_data
  236:             ($student->{'username'},$student->{'domain'},
  237:              $ENV{'request.course.id'});
  238:         &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
  239:                                                  'last student');
  240:     }
  241:     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
  242:     $r->rflush();
  243: }
  244: 
  245: ###############################################
  246: ###############################################
  247: 
  248: =pod 
  249: 
  250: =item &BuildProblemStatisticsPage()
  251: 
  252: Main interface to problem statistics.
  253: 
  254: =cut
  255: 
  256: ###############################################
  257: ###############################################
  258: sub BuildProblemStatisticsPage {
  259:     my ($r,$c)=@_;
  260:     #
  261:     $output_mode = 'html';
  262:     $show = 'grouped';
  263:     #
  264:     $r->print(&CreateInterface());
  265:     $r->print('<input type="hidden" name="statsfirstcall" value="no" />');
  266:     $r->print('<input type="hidden" name="sortby" value="'.$ENV{'form.sortby'}.
  267:               '" />');
  268:     if (! exists($ENV{'form.statsfirstcall'})) {
  269:         $r->print(<<ENDMSG);
  270: <p>
  271: <font size="+1">
  272: Please make your selections in the boxes above and hit 
  273: the button marked &quot;Update&nbsp;Display&quot;.
  274: </font>
  275: </p>
  276: ENDMSG
  277:         return;
  278:     }
  279:     #
  280:     &Gather_Student_Data($r);
  281:     #
  282:     #
  283:     if ($output_mode eq 'html') {
  284:         $r->print("<h2>".
  285:                   $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.
  286:                   "</h2>\n");
  287:         $r->print("<h3>".localtime(time)."</h3>");
  288:         $r->rflush();
  289:         if ($show eq 'grouped') {
  290:             &output_html_grouped_by_sequence($r);
  291:         } elsif ($show eq 'ungrouped') {
  292:             &output_html_ungrouped($r);
  293:         }
  294:     } elsif ($output_mode eq 'excel') {
  295:         $r->print("<h2>Preparing Excel Spreadsheet</h2>");
  296:         &output_excel($r);
  297:     } elsif ($output_mode eq 'plot') {
  298:         if ($show eq 'deg of diff') {
  299:             &plot_statistics($r,'DoDiff');
  300:         } elsif ($show eq 'per wrong') {
  301:             &plot_statistics($r,'%Wrng');
  302:         }
  303:     } else {
  304:         $r->print("<h1>Not implemented</h1>");
  305:     }
  306:     return;
  307: }
  308: 
  309: ###############################################
  310: ###############################################
  311: 
  312: =pod 
  313: 
  314: =item &output_html_grouped_by_sequence()
  315: 
  316: Presents the statistics data as an html table organized by the order
  317: the assessments appear in the course.
  318: 
  319: =cut
  320: 
  321: ###############################################
  322: ###############################################
  323: sub output_html_grouped_by_sequence {
  324:     my ($r) = @_;
  325:     my $problem_num = 0;
  326:     #$r->print(&ProblemStatisticsLegend());
  327:     my @Header = ("Title","Part","#Stdnts","Tries","Mod",
  328:                   "Mean","#YES","#yes","%Wrng","DoDiff",
  329:                   "S.D.","Skew.");#,"D.F.1st","D.F.2nd");
  330:     # #FFFFE6 #EEFFCC #DDFFFF FFDDDD #DDFFDD #FFDDFF
  331:     foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
  332:         next if ($sequence->{'num_assess'}<1);
  333:         $r->print("<h3>".$sequence->{'title'}."</h3>");
  334:         $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
  335:         $r->print('<table border="0" cellpadding="3">'."\n");
  336:         $r->print('<tr bgcolor="#FFFFE6"><th>'.
  337:                   join("</th><th>",@Header)."</th></tr>\n");
  338:         foreach my $resource (@{$sequence->{'contents'}}) {
  339:             next if ($resource->{'type'} ne 'assessment');
  340:             foreach my $part (@{$resource->{'parts'}}) {
  341:                 $problem_num++;
  342:                 my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
  343:                     $SKEW) = &Apache::loncoursedata::get_problem_statistics
  344:                         (undef,$resource->{'symb'},$part,
  345:                          $ENV{'request.course.id'});
  346:                 #
  347:                 $part = '&nbsp;' if ($part == 0);
  348:                 #
  349:                 my $wrongpercent = 0;
  350:                 if (defined($num) && $num > 0) {
  351:                     $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
  352:                 }
  353:                 my $option = '';
  354:                 $r->print('<tr>'.&statistics_html_table_data
  355:                           ($resource,$part,$num,$tries,$mod,$mean,$Solved,
  356:                            $solved,$wrongpercent,$DegOfDiff,$STD,$SKEW,
  357:                            $option).
  358:                           "</tr>\n");
  359:             }
  360:         }
  361:         $r->print("</table>\n");
  362:         $r->print("</td></tr></table>\n");
  363:         $r->rflush();
  364:     }
  365:     #
  366:     return;
  367: }
  368: 
  369: ###############################################
  370: ###############################################
  371: 
  372: =pod 
  373: 
  374: =item &output_html_ungrouped()
  375: 
  376: Presents the statistics data in a single html table which can be sorted by
  377: different columns.
  378: 
  379: =cut
  380: 
  381: ###############################################
  382: ###############################################
  383: sub output_html_ungrouped {
  384:     my ($r,$option) = @_;
  385:     #
  386:     my $problem_num = 0;
  387:     my $show_container = 0;
  388:     my $show_part = 0;
  389:     #$r->print(&ProblemStatisticsLegend());
  390:     my @Header = ("Title","Part","#Stdnts","Tries","Mod",
  391:                   "Mean","#YES","#yes","%Wrng","DoDiff",
  392:                   "S.D.","Skew");#,"D.F.1st","D.F.2nd");
  393:     #
  394:     my $sortby = undef;
  395:     foreach (@Header) {
  396:         if ($ENV{'form.sortby'} eq $_) {
  397:             $sortby = $_;
  398:         }
  399:     }
  400:     if (! defined($sortby) || $sortby eq '') {
  401:         $sortby = 'Container';
  402:     }
  403:     # If there is more than one sequence, list their titles
  404:     my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
  405:     if (@Sequences > 1) {
  406:         unshift(@Header,"Container");
  407:         $show_container = 1;
  408:     }
  409:     # 
  410:     # If the option for showing the problem number is needed, push that
  411:     # on the list too
  412:     if (defined($option) && $option =~ /show probnum/) {
  413:         unshift(@Header,"P#");
  414:     }
  415:     #
  416:     $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
  417:     $r->rflush();
  418:     #
  419:     # Compile the data
  420:     my @Statsarray;
  421:     foreach my $sequence (@Sequences) {
  422:         next if ($sequence->{'num_assess'}<1);
  423:         foreach my $resource (@{$sequence->{'contents'}}) {
  424:             next if ($resource->{'type'} ne 'assessment');
  425:             foreach my $part (@{$resource->{'parts'}}) {
  426:                 $problem_num++;
  427:                 my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
  428:                     $SKEW) = &Apache::loncoursedata::get_problem_statistics
  429:                         (undef,$resource->{'symb'},$part,
  430:                          $ENV{'request.course.id'});
  431:                 #
  432:                 $show_part = 1 if ($part ne '0');
  433:                 $part = '&nbsp;' if ($part == 0);
  434:                 #
  435:                 my $wrongpercent = 0;
  436:                 if (defined($num) && $num > 0) {
  437:                     $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
  438:                 }
  439:                 push (@Statsarray,
  440:                       { 'sequence' => $sequence,
  441:                         'resource' => $resource,
  442:                         'Title' => $resource->{'title'},
  443:                         'Part'  => $part,
  444:                         '#Stdnts' => $num,
  445:                         'Tries' => $tries,
  446:                         'Mod' => $mod,
  447:                         'Mean' => $mean,
  448:                         '#YES' => $Solved,
  449:                         '#yes' => $solved,
  450:                         '%Wrng' => $wrongpercent,
  451:                         'DoDiff' => $DegOfDiff,
  452:                         'S.D.' => $STD,
  453:                         'Skew' => $SKEW,
  454:                         'problem_num' => $problem_num,
  455:                       });
  456:             }
  457:         }
  458:     }
  459:     #
  460:     # Table Headers
  461:     $r->print('<table border="0" cellpadding="3">'."\n");
  462:     my $Str = '';
  463:     foreach (@Header) {
  464:         next if ($_ eq 'Part' && !$show_part);
  465:         # Do not allow sorting on some fields
  466:         if ($_ eq $sortby || /^(Part|P\#)$/) {  
  467:             $Str .= '<th>'.$_.'</th>';
  468:         } else {
  469:             $Str .= '<th>'.
  470:      '<a href="javascript:document.Statistics.sortby.value='."'$_'".
  471:          ';document.Statistics.submit();">'.
  472:              $_.'</a></th>';
  473:         }
  474:     }
  475:     $r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n");
  476:     #
  477:     # Sort the data
  478:     my @OutputOrder;
  479:     if ($sortby eq 'Container') {
  480:         @OutputOrder = @Statsarray;
  481:     } else {
  482:         # $sortby is already defined, so we can charge ahead
  483:         if ($sortby =~ /^(title|part)$/i) {
  484:             # Alpha comparison
  485:             @OutputOrder = sort {
  486:                 lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
  487:                     lc($a->{'Title'}) cmp lc($b->{'Title'}) ||
  488:                         lc($a->{'Part'}) cmp lc($b->{'Part'});
  489:             } @Statsarray;
  490:         } else {
  491:             # Numerical comparison
  492:             @OutputOrder = sort {
  493:                 my $retvalue = 0;
  494:                 if ($b->{$sortby} eq 'nan') {
  495:                     if ($a->{$sortby} ne 'nan') {
  496:                         $retvalue = -1;
  497:                     } else {
  498:                         $retvalue = 0;
  499:                     }
  500:                 }
  501:                 if ($a->{$sortby} eq 'nan') {
  502:                     if ($b->{$sortby} ne 'nan') {
  503:                         $retvalue = 1;
  504:                     }
  505:                 }
  506:                 if ($retvalue eq '0') {
  507:                     $retvalue = $b->{$sortby} <=> $a->{$sortby} ||
  508:                                 lc($a->{'Title'}) <=> lc($b->{'Title'}) ||
  509:                                 lc($a->{'Part'})  <=> lc($b->{'Part'});
  510:                 }
  511:                 $retvalue;
  512:             } @Statsarray;
  513:         }
  514:     }
  515:     $option .= ',no part' if (! $show_part);
  516:     foreach my $row (@OutputOrder) {
  517:         $r->print('<tr>');
  518:         if (defined($option) && $option =~ /show probnum/) {
  519:             $r->print('<td bgcolor="#FFFFE6">'.$row->{'problem_num'}.'</td>');
  520:         }
  521:         if ($show_container) {
  522:             $r->print('<td bgcolor="#FFFFE6">'
  523:                       .$row->{'sequence'}->{'title'}.'</td>');
  524:         }
  525:         $r->print(&statistics_html_table_data
  526:                   ($row->{'resource'},$row->{'Part'},$row->{'#Stdnts'},
  527:                    $row->{'Tries'},$row->{'Mod'},$row->{'Mean'},
  528:                    $row->{'#YES'},$row->{'#yes'},$row->{"\%Wrng"},
  529:                    $row->{'DoDiff'},$row->{'S.D.'},$row->{'Skew'},
  530:                    $option));
  531:         $r->print("</tr>\n");
  532:     }
  533:     $r->print("</table>\n");
  534:     $r->print("</td></tr></table>\n");
  535:     $r->rflush();
  536:     #
  537:     return;
  538: }
  539: 
  540: ###############################################
  541: ###############################################
  542: 
  543: =pod 
  544: 
  545: =item &output_excel()
  546: 
  547: Presents the statistical data in an Excel 95 compatable spreadsheet file.
  548: 
  549: =cut
  550: 
  551: ###############################################
  552: ###############################################
  553: sub output_excel {
  554:     my ($r) = @_;
  555:     my $filename = '/prtspool/'.
  556:         $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
  557:             time.'_'.rand(1000000000).'.xls';
  558:     #
  559:     my $excel_workbook = undef;
  560:     my $excel_sheet = undef;
  561:     #
  562:     my $rows_output = 0;
  563:     my $cols_output = 0;
  564:     #
  565:     # Create sheet
  566:     $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
  567:     #
  568:     # Check for errors
  569:     if (! defined($excel_workbook)) {
  570:         $r->log_error("Error creating excel spreadsheet $filename: $!");
  571:         $r->print("Problems creating new Excel file.  ".
  572:                   "This error has been logged.  ".
  573:                   "Please alert your LON-CAPA administrator");
  574:         return ;
  575:     }
  576:     #
  577:     # The excel spreadsheet stores temporary data in files, then put them
  578:     # together.  If needed we should be able to disable this (memory only).
  579:     # The temporary directory must be specified before calling 'addworksheet'.
  580:     # File::Temp is used to determine the temporary directory.
  581:     $excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
  582:     #
  583:     # Add a worksheet
  584:     my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
  585:     if (length($sheetname) > 31) {
  586:         $sheetname = substr($sheetname,0,31);
  587:     }
  588:     $excel_sheet = $excel_workbook->addworksheet($sheetname);
  589:     #
  590:     # Put the course description in the header
  591:     $excel_sheet->write($rows_output,$cols_output++,
  592:                    $ENV{'course.'.$ENV{'request.course.id'}.'.description'});
  593:     $cols_output += 3;
  594:     #
  595:     # Put a description of the sections listed
  596:     my $sectionstring = '';
  597:     my @Sections = @Apache::lonstatistics::SelectedSections;
  598:     if (scalar(@Sections) > 1) {
  599:         if (scalar(@Sections) > 2) {
  600:             my $last = pop(@Sections);
  601:             $sectionstring = "Sections ".join(', ',@Sections).', and '.$last;
  602:         } else {
  603:             $sectionstring = "Sections ".join(' and ',@Sections);
  604:         }
  605:     } else {
  606:         if ($Sections[0] eq 'all') {
  607:             $sectionstring = "All sections";
  608:         } else {
  609:             $sectionstring = "Section ".$Sections[0];
  610:         }
  611:     }
  612:     $excel_sheet->write($rows_output,$cols_output++,$sectionstring);
  613:     $cols_output += scalar(@Sections);
  614:     #
  615:     # Put the date in there too
  616:     $excel_sheet->write($rows_output,$cols_output++,
  617:                         'Compiled on '.localtime(time));
  618:     #
  619:     $rows_output++; 
  620:     $cols_output=0;
  621:     #
  622:     # Add the headers
  623:     my @Header = ("Container","Title","Part","#Stdnts","Tries","Mod",
  624:                   "Mean","#YES","#yes","%Wrng","DoDiff",
  625:                   "S.D.","Skew.");#,"D.F.1st","D.F.2nd");
  626:     foreach (@Header) {
  627:         $excel_sheet->write($rows_output,$cols_output++,$_);
  628:     }
  629:     $rows_output++;
  630:     #
  631:     # Write the data
  632:     foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
  633:         next if ($sequence->{'num_assess'}<1);
  634:         foreach my $resource (@{$sequence->{'contents'}}) {
  635:             next if ($resource->{'type'} ne 'assessment');
  636:             foreach my $part (@{$resource->{'parts'}}) {
  637:                 $cols_output=0;
  638:                 my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
  639:                     $SKEW) = &Apache::loncoursedata::get_problem_statistics
  640:                         (undef,$resource->{'symb'},$part,
  641:                          $ENV{'request.course.id'});
  642:                 #
  643:                 if (!defined($part) || $part eq '') {
  644:                     $part = ' ';
  645:                 }
  646:                 my $wrongpercent = 0;
  647:                 if (defined($num) && $num > 0) {
  648:                     $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
  649:                 }
  650:                 foreach ($sequence->{'title'},$resource->{'title'},$part,
  651:                          $num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
  652:                          $DegOfDiff,$STD,$SKEW) {
  653:                     $excel_sheet->write($rows_output,$cols_output++,$_);
  654:                 }
  655:                 $rows_output++;
  656:             }
  657:         }
  658:     }
  659:     #
  660:     # Write the excel file
  661:     $excel_workbook->close();
  662:     # Tell the user where to get their excel file
  663:     $r->print('<br />'.
  664:               '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
  665:     $r->rflush();
  666:     return;
  667: }
  668: 
  669: ###############################################
  670: ###############################################
  671: 
  672: =pod 
  673: 
  674: =item &statistics_html_table_data()
  675: 
  676: Help function used to format the rows for HTML table output.
  677: 
  678: =cut
  679: 
  680: ###############################################
  681: ###############################################
  682: sub statistics_html_table_data {
  683:     my ($resource,$part,$num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
  684:         $DegOfDiff,$STD,$SKEW,$options) = @_;
  685:     my $row = '';
  686:     $row .= '<td bgcolor="#FFFFE6">'.
  687:         '<a href="'.$resource->{'src'}.'" target="_blank" >'.
  688:             $resource->{'title'}.'</a>'.
  689:                 '</td>';
  690:     $row .= '<td bgcolor="#FFFFE6">'.$part.'</td>' if ($options !~ /no part/);
  691:     foreach ($num,$tries) {
  692:         $row .= '<td bgcolor="#EEFFCC" align="right">'.$_.'</td>';
  693:     }
  694:     foreach ($mod) {
  695:         $row .= '<td bgcolor="#DDFFFF" align="right">'.$_.'</td>';
  696:     }
  697:     foreach ($mean) {
  698:         $row .= '<td bgcolor="#DDFFFF" align="right">'.
  699:                   sprintf("%5.2f",$_).'</td>';
  700:     }
  701:     foreach ($Solved,$solved) {
  702:         $row .= '<td bgcolor="#DDFFFF" align="right">'.$_.'</td>';
  703:     }
  704:     foreach ($wrongpercent) {
  705:         $row .= '<td bgcolor="#DDFFFF" align="right">'.
  706:                   sprintf("%5.1f",$_).'</td>';
  707:     }
  708:     foreach ($DegOfDiff,$STD,$SKEW) {
  709:         $row .= '<td bgcolor="#FFDDDD" align="right">'.
  710:                   sprintf("%5.2f",$_).'</td>';
  711:     }
  712:     return $row;
  713: }
  714: 
  715: ###############################################
  716: ###############################################
  717: 
  718: =pod 
  719: 
  720: =item &plot_statistics()
  721: 
  722: =cut
  723: 
  724: ###############################################
  725: ###############################################
  726: sub plot_statistics {
  727:     my ($r,$datafield) = @_;
  728:     my @Data;
  729:     #
  730:     my %Fields = ('#Stdnts'=> 0,
  731:                   'Tries'  => 1,
  732:                   'Mod'    => 2,
  733:                   'Mean'   => 3,
  734:                   '#YES'   => 4,
  735:                   '#yes'   => 5,
  736:                   '%Wrng'  => 9,
  737:                   'DoDiff' => 6,
  738:                   'S.D.'   => 7,
  739:                   'Skew'   => 8,);
  740:     #
  741:     my $field = '%Wrng';
  742:     foreach (keys(%Fields)) {
  743:         $field = $_ if ($datafield eq $_);
  744:     }
  745:     my $fieldindex = $Fields{$field};
  746:     #
  747:     my $Max = 0;
  748:     foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
  749:         next if ($sequence->{'num_assess'}<1);
  750:         foreach my $resource (@{$sequence->{'contents'}}) {
  751:             next if ($resource->{'type'} ne 'assessment');
  752:             foreach my $part (@{$resource->{'parts'}}) {
  753:                 my @Results = &Apache::loncoursedata::get_problem_statistics
  754:                         (undef,$resource->{'symb'},$part,
  755:                          $ENV{'request.course.id'});
  756:                 my ($num,$Solved,$solved) = @Results[0,4,5];
  757:                 my $wrongpercent = 0;
  758:                 if (defined($num) && $num > 0) {
  759:                     $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
  760:                 }
  761:                 push (@Results,$wrongpercent);
  762:                 my $data = $Results[$fieldindex];
  763:                 $data = 0 if ($data eq 'nan');
  764:                 $Max = $data if ($Max<$data);
  765:                 push (@Data,$data);
  766:             }
  767:         }
  768:     }
  769:     #
  770:     # Print out plot request
  771:     my $title = 'Percent Wrong';
  772:     if ($field eq 'DoDiff') {
  773:         $title = 'Degree of Difficulty';
  774:     }
  775:     my $yaxis = 'Percent';
  776:     if ($field eq 'DoDiff') {
  777:         $yaxis = '';
  778:     } elsif ($field ne '%Wrng') {
  779:         $yaxis = '';
  780:     }
  781:     #
  782:     # Determine appropriate value for $Max
  783:     if ($field eq 'DoDiff') {
  784:         if ($Max > 0.5) {
  785:             $Max = 1;
  786:         } elsif ($Max > 0.2) {
  787:             $Max = 0.5;
  788:         } elsif ($Max > 0.1) {
  789:             $Max = 0.2;
  790:         }
  791:     } elsif ($field eq '%Wrng') {
  792:         if ($Max > 50) {
  793:             $Max = 100;
  794:         } elsif ($Max > 25) {
  795:             $Max = 50;
  796:         } elsif ($Max > 20) {
  797:             $Max = 25;
  798:         } elsif ($Max > 10) {
  799:             $Max = 20;
  800:         } elsif ($Max > 5) {
  801:             $Max = 10;
  802:         } else {
  803:             $Max = 5;
  804:         }
  805:     }
  806:     
  807:     $r->print("<p>".&DrawGraph(\@Data,$title,'Problem Number',$yaxis,
  808:                                $Max)."</p>\n");
  809:     #
  810:     # Print out the data
  811:     $ENV{'form.sortby'} = 'Contents';
  812:     &output_html_ungrouped($r,'show probnum');
  813:     return;
  814: }
  815: 
  816: ###############################################
  817: ###############################################
  818: 
  819: =pod 
  820: 
  821: =item &DrawGraph()
  822: 
  823: =cut
  824: 
  825: ###############################################
  826: ###############################################
  827: sub DrawGraph {
  828:     my ($values,$title,$xaxis,$yaxis,$Max)=@_;
  829:     $title = '' if (! defined($title));    
  830:     $xaxis = '' if (! defined($xaxis));
  831:     $yaxis = '' if (! defined($yaxis));
  832:     #
  833:     my $sendValues = join(',', @$values);
  834:     my $sendCount = scalar(@$values);
  835:     if ( $Max > 1 ) {
  836: 	if ($Max % 10) {
  837:             if ( int($Max) < $Max ) {
  838: 	    	$Max++;
  839: 		$Max = int($Max);
  840: 	    }
  841: 	}
  842:     } else { 
  843:         $Max = 1; 
  844:     }
  845:     my @GData = ($title,$xaxis,$yaxis,$Max,$sendCount,$sendValues);
  846:     return '<IMG src="/cgi-bin/graph.png?'.
  847:         (join('&', @GData)).'" border="1" />';
  848: }
  849: 
  850: ###############################################
  851: ###############################################
  852: 
  853: =pod 
  854: 
  855: =item &ProblemStatisticsLegend()
  856: 
  857: =cut
  858: 
  859: ###############################################
  860: ###############################################
  861: sub ProblemStatisticsLegend {
  862:     my $Ptr = '';
  863:     $Ptr = '<table border="0">';
  864:     $Ptr .= '<tr><td>';
  865:     $Ptr .= '<b>#Stdnts</b></td>';
  866:     $Ptr .= '<td>Total number of students attempted the problem.';
  867:     $Ptr .= '</td></tr><tr><td>';
  868:     $Ptr .= '<b>Tries</b></td>';
  869:     $Ptr .= '<td>Total number of tries for solving the problem.';
  870:     $Ptr .= '</td></tr><tr><td>';
  871:     $Ptr .= '<b>Mod</b></td>';
  872:     $Ptr .= '<td>Largest number of tries for solving the problem by a student.';
  873:     $Ptr .= '</td></tr><tr><td>';
  874:     $Ptr .= '<b>Mean</b></td>';
  875:     $Ptr .= '<td>Average number of tries. [ Tries / #Stdnts ]';
  876:     $Ptr .= '</td></tr><tr><td>';
  877:     $Ptr .= '<b>#YES</b></td>';
  878:     $Ptr .= '<td>Number of students solved the problem correctly.';
  879:     $Ptr .= '</td></tr><tr><td>';
  880:     $Ptr .= '<b>#yes</b></td>';
  881:     $Ptr .= '<td>Number of students solved the problem by override.';
  882:     $Ptr .= '</td></tr><tr><td>';
  883:     $Ptr .= '<b>%Wrong</b></td>';
  884:     $Ptr .= '<td>Percentage of students who tried to solve the problem ';
  885:     $Ptr .= 'but is still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';
  886:     $Ptr .= '</td></tr><tr><td>';
  887:     $Ptr .= '<b>DoDiff</b></td>';
  888:     $Ptr .= '<td>Degree of Difficulty of the problem.  ';
  889:     $Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';
  890:     $Ptr .= '</td></tr><tr><td>';
  891:     $Ptr .= '<b>S.D.</b></td>';
  892:     $Ptr .= '<td>Standard Deviation of the tries.  ';
  893:     $Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';
  894:     $Ptr .= 'where Xi denotes every student\'s tries ]';
  895:     $Ptr .= '</td></tr><tr><td>';
  896:     $Ptr .= '<b>Skew.</b></td>';
  897:     $Ptr .= '<td>Skewness of the students tries.';
  898:     $Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';
  899:     $Ptr .= '</td></tr><tr><td>';
  900:     $Ptr .= '<b>Dis.F.</b></td>';
  901:     $Ptr .= '<td>Discrimination Factor: A Standard for evaluating the ';
  902:     $Ptr .= 'problem according to a Criterion<br>';
  903:     $Ptr .= '<b>[Criterion to group students into %27 Upper Students - ';
  904:     $Ptr .= 'and %27 Lower Students]</b><br>';
  905:     $Ptr .= '<b>1st Criterion</b> for Sorting the Students: ';
  906:     $Ptr .= '<b>Sum of Partial Credit Awarded / Total Number of Tries</b><br>';
  907:     $Ptr .= '<b>2nd Criterion</b> for Sorting the Students: ';
  908:     $Ptr .= '<b>Total number of Correct Answers / Total Number of Tries</b>';
  909:     $Ptr .= '</td></tr>';
  910:     $Ptr .= '<tr><td><b>Disc.</b></td>';
  911:     $Ptr .= '<td>Number of Students had at least one discussion.';
  912:     $Ptr .= '</td></tr></table>';
  913:     return $Ptr;
  914: }
  915: 
  916: #---- END Problem Statistics Web Page ----------------------------------------
  917: 
  918: 1;
  919: __END__

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