File:  [LON-CAPA] / loncom / interface / statistics / lonstudentassessment.pm
Revision 1.32: download - view: text, annotated - select for diffs
Mon Mar 3 22:54:05 2003 UTC (21 years, 4 months ago) by matthew
Branches: MAIN
CVS tags: HEAD
Implemented Excel output.  Should be pretty much done.  Two output modes.
One gives score and max possible for each sequence for each student.
Second gives only scores for each sequence for each student, saving columns.
Beginning to change output selector.

    1: # The LearningOnline Network with CAPA
    2: #
    3: # $Id: lonstudentassessment.pm,v 1.32 2003/03/03 22:54:05 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: # LON-CAPA is free software; you can redistribute it and/or modify
    9: # it under the terms of the GNU General Public License as published by
   10: # the Free Software Foundation; either version 2 of the License, or
   11: # (at your option) any later version.
   12: #
   13: # LON-CAPA is distributed in the hope that it will be useful,
   14: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   15: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16: # GNU General Public License for more details.
   17: #
   18: # You should have received a copy of the GNU General Public License
   19: # along with LON-CAPA; if not, write to the Free Software
   20: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   21: #
   22: # /home/httpd/html/adm/gpl.txt
   23: #
   24: # http://www.lon-capa.org/
   25: #
   26: # (Navigate problems for statistical reports
   27: #
   28: #######################################################
   29: #######################################################
   30: 
   31: =pod
   32: 
   33: =head1 NAME
   34: 
   35: lonstudentassessment
   36: 
   37: =head1 SYNOPSIS
   38: 
   39: Presents assessment data about a student or a group of students.
   40: 
   41: =head1 Subroutines
   42: 
   43: =over 4 
   44: 
   45: =cut
   46: 
   47: #######################################################
   48: #######################################################
   49: 
   50: package Apache::lonstudentassessment;
   51: 
   52: use strict;
   53: use Apache::lonstatistics;
   54: use Apache::lonhtmlcommon;
   55: use Apache::loncoursedata;
   56: use Apache::lonnet; # for logging porpoises
   57: use Spreadsheet::WriteExcel;
   58: 
   59: #######################################################
   60: #######################################################
   61: =pod
   62: 
   63: =item Package Variables
   64: 
   65: =over 4
   66: 
   67: =item $Statistics Hash ref to store student data.  Indexed by symb,
   68:       contains hashes with keys 'score' and 'max'.
   69: 
   70: =cut
   71: 
   72: #######################################################
   73: #######################################################
   74: 
   75: my $Statistics;
   76: 
   77: #######################################################
   78: #######################################################
   79: 
   80: =pod
   81: 
   82: =item $show_links 'yes' or 'no' for linking to student performance data
   83: 
   84: =item $output_mode 'html', 'excel', or 'csv' for output mode
   85: 
   86: =item $show 'all', 'totals', or 'scores' determines how much data is output
   87: 
   88: =cut
   89: 
   90: #######################################################
   91: #######################################################
   92: my $show_links;
   93: my $output_mode;
   94: my $show;
   95: 
   96: #######################################################
   97: #######################################################
   98: # End of package variable declarations
   99: 
  100: =pod
  101: 
  102: =back
  103: 
  104: =cut
  105: 
  106: #######################################################
  107: #######################################################
  108: 
  109: =pod
  110: 
  111: =item &BuildStudentAssessmentPage()
  112: 
  113: Inputs: 
  114: 
  115: =over 4
  116: 
  117: =item $r Apache Request
  118: 
  119: =item $c Apache Connection 
  120: 
  121: =back
  122: 
  123: =cut
  124: 
  125: #######################################################
  126: #######################################################
  127: sub BuildStudentAssessmentPage {
  128:     my ($r,$c)=@_;
  129:     undef($Statistics);
  130:     #
  131:     # Print out the HTML headers for the interface
  132:     #    This also parses the output mode selector
  133:     #    This step must always be done.
  134:     $r->print(&CreateInterface());
  135:     $r->print('<input type="hidden" name="notfirstrun" value="true" />');
  136:     $r->rflush();
  137:     if (! exists($ENV{'form.notfirstrun'})) {
  138:         $r->print(<<ENDMSG);
  139: <p>
  140: <font size="+1">
  141: Please make your selections in the boxes above and hit 
  142: the button marked &quot;Update&nbsp;Display&quot;.
  143: </font>
  144: </p>
  145: ENDMSG
  146:         return;
  147:     }
  148:     #
  149:     #
  150:     my $initialize     = \&html_initialize;
  151:     my $output_student = \&html_outputstudent;
  152:     my $finish         = \&html_finish;
  153:     #
  154:     if ($output_mode eq 'excel') {
  155:         $initialize     = \&excel_initialize;
  156:         $output_student = \&excel_outputstudent;
  157:         $finish         = \&excel_finish;
  158:     } elsif ($output_mode eq 'csv') {
  159:         $initialize     = \&csv_initialize;
  160:         $output_student = \&csv_outputstudent;
  161:         $finish         = \&csv_finish;
  162:     }
  163:     #
  164:     if($c->aborted()) {  return ; }
  165:     #
  166:     # Call the initialize routine selected above
  167:     $initialize->($r);
  168:     foreach my $student (@Apache::lonstatistics::Students) {
  169:         if($c->aborted()) { 
  170:             $finish->($r);
  171:             return ; 
  172:         }
  173:         # Call the output_student routine selected above
  174:         $output_student->($r,$student);
  175:     }
  176:     # Call the "finish" routine selected above
  177:     $finish->($r);
  178:     #
  179:     return;
  180: }
  181: 
  182: #######################################################
  183: #######################################################
  184: 
  185: sub get_student_fields_to_show {
  186:     my @to_show = @Apache::lonstatistics::SelectedStudentData;
  187:     foreach (@to_show) {
  188:         if ($_ eq 'all') {
  189:             @to_show = @Apache::lonstatistics::StudentDataOrder;
  190:             last;
  191:         }
  192:     }
  193:     return @to_show;
  194: }
  195: 
  196: sub get_sequences_to_show {
  197:     my @Sequences;
  198:     foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
  199:         foreach my $sequence (@Apache::lonstatistics::Sequences) {
  200:             next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all');
  201:             next if ($sequence->{'num_assess'} < 1);
  202:             push (@Sequences,$sequence);
  203:         }
  204:     }
  205:     return @Sequences;
  206: }
  207: 
  208: 
  209: #######################################################
  210: #######################################################
  211: 
  212: =pod
  213: 
  214: =item &CreateInterface()
  215: 
  216: Called by &BuildStudentAssessmentPage to create the top part of the
  217: page which displays the chart.
  218: 
  219: Inputs: None
  220: 
  221: Returns:  A string containing the HTML for the headers and top table for 
  222: the chart page.
  223: 
  224: =cut
  225: 
  226: #######################################################
  227: #######################################################
  228: sub CreateInterface {
  229:     my $Str = '';
  230: #    $Str .= &CreateLegend();
  231:     $Str .= '<table cellspacing="5">'."\n";
  232:     $Str .= '<tr>';
  233:     $Str .= '<td align="center"><b>Sections</b></td>';
  234:     $Str .= '<td align="center"><b>Student Data</b></td>';
  235:     $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
  236:     $Str .= '<td align="center"><b>Output Format</b></td>';
  237:     $Str .= '</tr>'."\n";
  238:     #
  239:     $Str .= '<tr><td align="center">'."\n";
  240:     $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
  241:     $Str .= '</td><td align="center">';
  242:     my $only_seq_with_assessments = sub { 
  243:         my $s=shift;
  244:         if ($s->{'num_assess'} < 1) { 
  245:             return 0;
  246:         } else { 
  247:             return 1;
  248:         }
  249:     };
  250:     $Str .= &Apache::lonstatistics::StudentDataSelect('StudentData','multiple',
  251:                                                       5,undef);
  252:     $Str .= '</td><td>'."\n";
  253:     $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
  254:                                               $only_seq_with_assessments);
  255:     $Str .= '</td><td>'."\n";
  256:     $Str .= &CreateAndParseOutputSelector();
  257:     $Str .= '</td></tr>'."\n";
  258:     $Str .= '</table>'."\n";
  259:     return $Str;
  260: }
  261: 
  262: #######################################################
  263: #######################################################
  264: 
  265: =pod
  266: 
  267: =item &CreateAndParseOutputSelector()
  268: 
  269: =cut
  270: 
  271: #######################################################
  272: #######################################################
  273: my @OutputOptions = 
  274:     ({ name  => 'HTML, with links',
  275:        value => 'html, with links',
  276:        description => ''},
  277:      { name  => 'HTML, without links',
  278:        value => 'html, without links',
  279:        description => ''},
  280:      { name  => 'HTML, totals',
  281:        value => 'html, totals',
  282:        description => ''},
  283:      { name  => 'HTML, scores only',
  284:        value => 'html, scores only',
  285:        description => ''},
  286:      { name  => 'Excel, totals',
  287:        value => 'excel, totals',
  288:        description => ''},
  289:      { name  => 'Excel, scores only',
  290:        value => 'excel, scores only',
  291:        description => ''},
  292:      { name  => 'CSV, totals',
  293:        value => 'csv, totals',
  294:        description => ''},
  295:      { name  => 'CSV, scores only',
  296:        value => 'csv, scores only',
  297:        description => ''},
  298:      { name  => 'CSV, everything',
  299:        value => 'csv, everything',
  300:        description => ''}
  301:      );
  302: 
  303: sub CreateAndParseOutputSelector {
  304:     my $Str = '';
  305:     my $elementname = 'outputmode';
  306:     #
  307:     # Format for output options is 'mode, restrictions';
  308:     my $selected = 'html, with links';
  309:     if (exists($ENV{'form.'.$elementname})) {
  310:         if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
  311:             $selected = $ENV{'form.'.$elementname}->[0];
  312:         } else {
  313:             $selected = $ENV{'form.'.$elementname};
  314:         }
  315:     }
  316:     #
  317:     # Set package variables describing output mode
  318:     $show_links  = 'no';
  319:     $output_mode = 'html';
  320:     $show        = 'all';
  321:     my ($mode,$restriction) = split(',',$selected);
  322:     $restriction =~ s/^\s*//;
  323:     if ($mode =~ /^(html|excel|csv)$/) {
  324:         $output_mode = $mode;
  325:     } else {
  326:         $output_mode = 'html';
  327:     }
  328:     if ($restriction eq 'with links') {
  329:         $show_links = 'yes';
  330:     } else {
  331:         $show_links = 'no';
  332:     }
  333:     if ($restriction eq 'totals') {
  334:         $show = 'totals';
  335:     } elsif ($restriction eq 'scores only') {
  336:         $show = 'scores';
  337:     } else {
  338:         $show = 'everything';
  339:     }
  340:     #
  341:     # Build the form element
  342:     $Str = qq/<select size="5" name="$elementname">/;
  343:     foreach my $option (@OutputOptions) {
  344:         $Str .= "\n".'    <option value="'.$option->{'value'}.'"';
  345:         $Str .= " selected " if ($option->{'value'} eq $selected);
  346:         $Str .= ">".$option->{'name'}."<\/option>";
  347:     }
  348:     $Str .= "\n</select>";
  349:     return $Str;
  350: }
  351: 
  352: #######################################################
  353: #######################################################
  354: 
  355: =pod
  356: 
  357: =head2 HTML output routines
  358: 
  359: =item &html_initialize($r)
  360: 
  361: Create labels for the columns of student data to show.
  362: 
  363: =item &html_outputstudent($r,$student)
  364: 
  365: Return a line of the chart for a student.
  366: 
  367: =item &html_finish($r)
  368: 
  369: =cut
  370: 
  371: #######################################################
  372: #######################################################
  373: {
  374:     my $padding;
  375:     my $count;
  376: 
  377: sub html_initialize {
  378:     my ($r) = @_;
  379:     #
  380:     $padding = ' 'x3;
  381:     $count = 1;
  382:     #
  383:     my $Str = "<pre>\n";
  384:     # First, the @StudentData fields need to be listed
  385:     my @to_show = &get_student_fields_to_show();
  386:     foreach my $field (@to_show) {
  387:         my $title=$Apache::lonstatistics::StudentData{$field}->{'title'};
  388:         my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'};
  389:         my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
  390:         $Str .= $title.' 'x($width-$base).$padding;
  391:     }
  392:     # Now the selected sequences need to be listed
  393:     foreach my $sequence (&get_sequences_to_show) {
  394:         my $title = $sequence->{'title'};
  395:         my $base  = $sequence->{'base_width'};
  396:         my $width = $sequence->{'width'};
  397:         $Str .= $title.' 'x($width-$base).$padding;
  398:     }
  399:     $Str .= "total (of shown problems)</pre>\n";
  400:     $Str .= "<pre>";
  401:     $r->print($Str);
  402:     $r->rflush();
  403:     return;
  404: }
  405: 
  406: sub html_outputstudent {
  407:     my ($r,$student) = @_;
  408:     my $Str = '';
  409:     # First, the @StudentData fields need to be listed
  410:     my @to_show = &get_student_fields_to_show();
  411:     foreach my $field (@to_show) {
  412:         my $title=$student->{$field};
  413:         my $base = length($title);
  414:         my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
  415:         $Str .= $title.' 'x($width-$base).$padding;
  416:     }
  417:     # Get ALL the students data
  418:     my %StudentsData;
  419:     my @tmp = &Apache::loncoursedata::get_current_state
  420:         ($student->{'username'},$student->{'domain'},undef,
  421:          $ENV{'request.course.id'});
  422:     if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
  423:         %StudentsData = @tmp;
  424:     }
  425:     if (scalar(@tmp) < 1) {
  426:         $Str .= '<font color="blue">No Course Data</font>'."\n";
  427:         $r->print($Str);
  428:         $r->rflush();
  429:         return;
  430:     }
  431:     #
  432:     # By sequence build up the data
  433:     my $studentstats;
  434:     my $PerformanceStr = '';
  435:     foreach my $seq (&get_sequences_to_show) {
  436:         my ($performance,$score,$seq_max) =
  437:             &StudentPerformanceOnSequence($student,\%StudentsData,
  438:                                           $seq,$show_links);
  439:         my $ratio = $score.'/'.$seq_max;
  440:         #
  441:         if ($show eq 'totals') {
  442:             $performance = ' 'x(length($seq_max)-length($score)).$ratio;
  443:             $performance .= ' 'x($seq->{'width'}-length($performance));
  444:         } elsif ($show eq 'scores') {
  445:             $performance = $score;
  446:             $performance .= ' 'x($seq->{'width'}-length($performance));
  447:         } else {
  448:             # Pad with extra spaces
  449:             $performance .= ' 'x($seq->{'width'}-$seq_max-
  450:                                  length($ratio)
  451:                                  ).$ratio;
  452:         }
  453:         #
  454:         $Str .= $performance.$padding;
  455:         #
  456:         $studentstats->{$seq->{'symb'}}->{'score'}= $score;
  457:         $studentstats->{$seq->{'symb'}}->{'max'}  = $seq_max;
  458:     }
  459:     #
  460:     # Total it up and store the statistics info.
  461:     my ($score,$max) = (0,0);
  462:     while (my ($symb,$seq_stats) = each (%{$studentstats})) {
  463:         $Statistics->{$symb}->{'score'} += $seq_stats->{'score'};
  464:         $Statistics->{$symb}->{'max'}   += $seq_stats->{'max'};
  465:         $score += $seq_stats->{'score'};
  466:         $max   += $seq_stats->{'max'};
  467:     }
  468:     $Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max;
  469:     $Str .= " \n";
  470:     $r->print($Str);
  471:     #
  472:     $count++;
  473:     if($count % 5 == 0) {
  474:         $r->print("</pre><pre>");
  475:     }
  476:     #
  477:     $r->rflush();
  478:     return;
  479: }    
  480: 
  481: sub html_finish {
  482:     my ($r) = @_;
  483:     $r->print("</pre>\n"); 
  484:     $r->rflush();
  485:     return;
  486: }
  487: 
  488: }
  489: 
  490: #######################################################
  491: #######################################################
  492: 
  493: =pod
  494: 
  495: =head2 EXCEL subroutines
  496: 
  497: =item &excel_initialize($r)
  498: 
  499: =item &excel_outputstudent($r,$student)
  500: 
  501: =item &excel_finish($r)
  502: 
  503: =cut
  504: 
  505: #######################################################
  506: #######################################################
  507: {
  508: 
  509: my $excel_sheet;
  510: my $excel_workbook;
  511: 
  512: my $filename;
  513: my $rows_output;
  514: my $cols_output;
  515: 
  516: my $num_students;
  517: my $start_time;
  518: 
  519: sub excel_initialize {
  520:     my ($r) = @_;
  521:     #
  522:     $filename = '/prtspool/'.
  523:         $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
  524:             time.'_'.rand(1000000000).'.xls';
  525:     #
  526:     $excel_workbook = undef;
  527:     $excel_sheet = undef;
  528:     #
  529:     $rows_output = 0;
  530:     $cols_output = 0;
  531:     #
  532:     $num_students = 0;
  533:     $start_time   = time;
  534:     #
  535:     # Create sheet
  536:     $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
  537:     #
  538:     # Check for errors
  539:     if (! defined($excel_workbook)) {
  540:         $r->log_error("Error creating excel spreadsheet $filename: $!");
  541:         $r->print("Problems creating new Excel file.  ".
  542:                   "This error has been logged.  ".
  543:                   "Please alert your LON-CAPA administrator");
  544:         return ;
  545:     }
  546:     #
  547:     # The excel spreadsheet stores temporary data in files, then put them
  548:     # together.  If needed we should be able to disable this (memory only).
  549:     # The temporary directory must be specified before calling 'addworksheet'.
  550:     # File::Temp is used to determine the temporary directory.
  551:     $excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
  552:     #
  553:     # Add a worksheet
  554:     $excel_sheet = $excel_workbook->addworksheet
  555:         ($ENV{'course.'.$ENV{'request.course.id'}.'.description'});
  556:     #
  557:     # Add the student headers
  558:     foreach my $field (&get_student_fields_to_show()) {
  559:         $excel_sheet->write(1,$cols_output++,$field);
  560:     }
  561:     #
  562:     # Add the Sequence Headers
  563:     foreach my $seq (&get_sequences_to_show) {
  564:         $excel_sheet->write(0,$cols_output,$seq->{'title'});
  565:         if ($show eq 'totals') {
  566:             $excel_sheet->write(1,$cols_output,'score');
  567:             $excel_sheet->write(1,$cols_output+1,'maximum');
  568:             $cols_output += 2;
  569:         } else {
  570:             $cols_output++;
  571:         }
  572:     }
  573:     #
  574:     # Bookkeeping
  575:     if ($show eq 'totals') {
  576:         $rows_output = 2;
  577:     } else {
  578:         $rows_output = 1;
  579:     }
  580:     #
  581:     # Let the user know what we are doing
  582:     my $studentcount = scalar(@Apache::lonstatistics::Students); 
  583:     $r->print("<h1>Compiling Excel spreadsheet for ".
  584:               $studentcount.' student');
  585:     $r->print('s') if ($studentcount > 1);
  586:     $r->print("</h1>\n");
  587:     $r->rflush();
  588:     #
  589:     return;
  590: }
  591: 
  592: sub excel_outputstudent {
  593:     my ($r,$student) = @_;
  594:     return if (! defined($excel_sheet));
  595:     $cols_output=0;
  596:     #
  597:     # Write out student data
  598:     my @to_show = &get_student_fields_to_show();
  599:     foreach my $field (@to_show) {
  600:         $excel_sheet->write($rows_output,$cols_output++,$student->{$field});
  601:     }
  602:     #
  603:     # Get student assessment data
  604:     my %StudentsData;
  605:     my @tmp = &Apache::loncoursedata::get_current_state($student->{'username'},
  606:                                                         $student->{'domain'},
  607:                                                         undef,
  608:                                                    $ENV{'request.course.id'});
  609:     if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
  610:         %StudentsData = @tmp;
  611:     }
  612:     #
  613:     # Write out sequence scores and totals data
  614:     foreach my $seq (&get_sequences_to_show) {
  615:         my ($performance,$score,$seq_max) =
  616:             &StudentPerformanceOnSequence($student,\%StudentsData,
  617:                                           $seq,'no');
  618:         if ($show eq 'totals' || $show eq 'scores') {
  619:             $excel_sheet->write($rows_output,$cols_output++,$score);
  620:         }
  621:         if ($show eq 'totals') {
  622:             $excel_sheet->write($rows_output,$cols_output++,$seq_max);
  623:         }
  624:     }
  625:     #
  626:     # Bookkeeping
  627:     $rows_output++; 
  628:     $cols_output=0;
  629:     #
  630:     # Time estimate
  631:     $num_students++;
  632:     if ($num_students % 10 == 0) {
  633:         my $time_est = (time - $start_time)/$num_students * 
  634:             (scalar(@Apache::lonstatistics::Students)-$num_students);
  635:         $time_est = int($time_est);
  636:         if (int ($time_est/60) > 0) {
  637:             my $min = int($time_est/60);
  638:             my $sec = $time_est % 60;
  639:             $time_est = $min.' minutes';
  640:             if ($sec > 1) {
  641:                 $time_est.= ', '.$sec.' seconds';
  642:             } elsif ($sec > 0) {
  643:                 $time_est.= ', '.$sec.' second';
  644:             }
  645:         } else {
  646:             $time_est .= ' seconds';
  647:         }
  648:         $r->print($num_students.' out of '.
  649:                   (scalar(@Apache::lonstatistics::Students)).
  650:                   " students processed.  ".
  651:                   $time_est." remain.  <br />\n");
  652:         $r->rflush();
  653:     }
  654:     return;
  655: }
  656: 
  657: sub excel_finish {
  658:     my ($r) = @_;
  659:     return if (! defined($excel_sheet));
  660:     #
  661:     # Write the excel file
  662:     $excel_workbook->close();
  663:     my $c = $r->connection();
  664:     #
  665:     return if($c->aborted());
  666:     #
  667:     # Tell the user where to get their excel file
  668:     $r->print('<br /><br />'.
  669:               '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
  670:     my $total_time = time - $start_time;
  671:     if (int ($total_time / 60) > 0) {
  672:         $total_time = int($total_time/60).' minutes, '.($total_time % 60);
  673:     }
  674:     $r->print('<br />'.$total_time.' seconds total');
  675:     $r->rflush();
  676:     return;
  677: }
  678: 
  679: }
  680: #######################################################
  681: #######################################################
  682: 
  683: =pod
  684: 
  685: =head2 CSV output routines
  686: 
  687: =item &csv_initialize($r)
  688: 
  689: =item &csv_outputstudent($r,$student)
  690: 
  691: =item &csv_finish($r)
  692: 
  693: =cut
  694: 
  695: #######################################################
  696: #######################################################
  697: {
  698: 
  699: sub csv_initialize{
  700:     my ($r) = @_;
  701:     $r->print("<h1>Not implemented yet</h1>");
  702:     return;
  703: }
  704: 
  705: sub csv_outputstudent {
  706:     my ($r,$student) = @_;
  707: }
  708: 
  709: sub csv_finish {
  710:     my ($r) = @_;
  711: }
  712: 
  713: }
  714: 
  715: #######################################################
  716: #######################################################
  717: 
  718: =pod
  719: 
  720: =item &StudentPerformanceOnSequence()
  721: 
  722: Inputs:
  723: 
  724: =over 4
  725: 
  726: =item $student
  727: 
  728: =item $studentdata Hash ref to all student data
  729: 
  730: =item $seq Hash ref, the sequence we are working on
  731: 
  732: =item $links if defined we will output links to each resource.
  733: 
  734: =back
  735: 
  736: =cut
  737: 
  738: #######################################################
  739: #######################################################
  740: sub StudentPerformanceOnSequence {
  741:     my ($student,$studentdata,$seq,$links) = @_;
  742:     $links = 'no' if (! defined($links));
  743:     my $Str = '';
  744:     my ($sum,$max) = (0,0);
  745:     foreach my $resource (@{$seq->{'contents'}}) {
  746:         next if ($resource->{'type'} ne 'assessment');
  747:         my $resource_data = $studentdata->{$resource->{'symb'}};
  748:         my $value = '';
  749:         foreach my $partnum (@{$resource->{'parts'}}) {
  750:             $max++;
  751:             my $symbol = ' '; # default to space
  752:             #
  753:             if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {
  754:                 my $status = $resource_data->{'resource.'.$partnum.'.solved'};
  755:                 if ($status eq 'correct_by_override') {
  756:                     $symbol = '+';
  757:                     $sum++;
  758:                 } elsif ($status eq 'incorrect_by_override') {
  759:                     $symbol = '-';
  760:                 } elsif ($status eq 'ungraded_attempted') {
  761:                     $symbol = '#';
  762:                 } elsif ($status eq 'incorrect_attempted')  {
  763:                     $symbol = '.';
  764:                 } elsif ($status eq 'excused') {
  765:                     $symbol = 'x';
  766:                     $max--;
  767:                 } elsif ($status eq 'correct_by_student' &&
  768:                     exists($resource_data->{'resource.'.$partnum.'.tries'})){
  769:                     my $num = $resource_data->{'resource.'.$partnum.'.tries'};
  770:                     if ($num > 9) {
  771:                         $symbol = '*';
  772:                     } elsif ($num > 0) {
  773:                         $symbol = $num;
  774:                     } else {
  775:                         $symbol = ' ';
  776:                     }
  777:                     $sum++;
  778:                 } else {
  779:                     $symbol = ' ';
  780:                 }
  781:             } else {
  782:                 # Unsolved.  Did they try?
  783:                 if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
  784:                     $symbol = '.';
  785:                 } else {
  786:                     $symbol = ' ';
  787:                 }
  788:             }
  789:             #
  790:             if ($links eq 'yes' && $symbol ne ' ') {
  791:                 $symbol = '<a href="/adm/grades'.
  792:                     '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
  793:                         '&student='.$student->{'username'}.
  794:                             '&domain='.$student->{'domain'}.
  795:                                 '&command=submission">'.$symbol.'</a>';
  796:             }
  797:             $value .= $symbol;
  798:         }
  799:         $Str .= $value;
  800:     }
  801:     return ($Str,$sum,$max);
  802: }
  803: 
  804: #######################################################
  805: #######################################################
  806: sub StudentAverageTotal {
  807:     my ($cache, $students, $sequenceKeys)=@_;
  808:     my $Str = "\n<b>Summary Tables:</b>\n";
  809:     my %Correct = ();
  810:     my $ProblemsSolved = 0;
  811:     my $TotalProblems = 0;
  812:     my $StudentCount = 0;
  813: 
  814:     foreach my $name (@$students) {
  815:         $StudentCount++;
  816:         foreach my $sequence (@$sequenceKeys) {
  817:             $Correct{$sequence} +=
  818: 	       $cache->{$name.':'.$sequence.':problemsCorrect'};
  819:         }
  820: 	$ProblemsSolved += $cache->{$name.':problemsSolved'};
  821:         $TotalProblems += $cache->{$name.':totalProblems'};
  822:     }
  823:     if ($StudentCount) { 
  824:         $ProblemsSolved = sprintf( "%.2f", 
  825:                              $ProblemsSolved/$StudentCount);
  826:         $TotalProblems /= $StudentCount;
  827:     } else {
  828:         $ProblemsSolved = 0;
  829:         $TotalProblems  = 0;
  830:     }
  831: 
  832:     $Str .= '<table border=2 cellspacing="1">'."\n";
  833:     $Str .= '<tr><td><b>Students Count</b></td><td><b>'.
  834:             $StudentCount.'</b></td></tr>'."\n";
  835:     $Str .= '<tr><td><b>Total Problems</b></td><td><b>'.
  836:             $TotalProblems.'</b></td></tr>'."\n";
  837:     $Str .= '<tr><td><b>Average Correct</b></td><td><b>'.
  838:             $ProblemsSolved.'</b></td></tr>'."\n";
  839:     $Str .= '</table>'."\n";
  840: 
  841:     $Str .= '<table border=2 cellspacing="1">'."\n";
  842:     $Str .= '<tr><th>Title</th><th>Total Problems</th>'.
  843:             '<th>Average Correct</th></tr>'."\n";
  844:     foreach my $S(@$sequenceKeys) {
  845:         my $title=$cache->{$S.':title'};
  846: 	#$Str .= $cache->{$S.':problems'};
  847: 	#my @problems=split(':', $cache->{$S.':problems'});
  848: 	#my $pCount=scalar @problems;
  849: 	my $pCount=MaxSeqPr($cache,@$students[0],$S);
  850:         my $crr;
  851: 	if ($StudentCount) {
  852:             $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
  853:         } else {
  854:             $crr="0.00";
  855:         }
  856:         $Str .= '<tr><td>'.$title.
  857:                 '</td><td align=center>'.$pCount.
  858:                 '</td><td align=center>'.$crr.
  859:                 '</td></tr>'."\n";
  860:     }
  861: 
  862:     $Str .= '</table>'."\n";
  863: 
  864:     return $Str;
  865: }
  866: 
  867: #######################################################
  868: #######################################################
  869: 
  870: =pod
  871: 
  872: =item &CreateLegend()
  873: 
  874: This function returns a formatted string containing the legend for the
  875: chart.  The legend describes the symbols used to represent grades for
  876: problems.
  877: 
  878: =cut
  879: 
  880: #######################################################
  881: #######################################################
  882: sub CreateLegend {
  883:     my $Str = "<p><pre>".
  884:               "   1  correct by student in 1 try\n".
  885:               "   7  correct by student in 7 tries\n".
  886:               "   *  correct by student in more than 9 tries\n".
  887: 	      "   +  correct by hand grading or override\n".
  888:               "   -  incorrect by override\n".
  889: 	      "   .  incorrect attempted\n".
  890: 	      "   #  ungraded attempted\n".
  891:               "      not attempted (blank field)\n".
  892: 	      "   x  excused".
  893:               "</pre><p>";
  894:     return $Str;
  895: }
  896: 
  897: #######################################################
  898: #######################################################
  899: 
  900: =pod 
  901: 
  902: =back
  903: 
  904: =cut
  905: 
  906: #######################################################
  907: #######################################################
  908: 
  909: 1;
  910: 
  911: __END__

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