Annotation of loncom/interface/statistics/lonstudentassessment.pm, revision 1.32

1.1       stredwic    1: # The LearningOnline Network with CAPA
                      2: #
1.32    ! matthew     3: # $Id: lonstudentassessment.pm,v 1.31 2003/03/03 19:28:29 matthew Exp $
1.1       stredwic    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
1.28      matthew    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: #######################################################
1.1       stredwic   49: 
1.21      minaeibi   50: package Apache::lonstudentassessment;
1.1       stredwic   51: 
                     52: use strict;
1.28      matthew    53: use Apache::lonstatistics;
1.1       stredwic   54: use Apache::lonhtmlcommon;
                     55: use Apache::loncoursedata;
1.28      matthew    56: use Apache::lonnet; # for logging porpoises
1.31      matthew    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: #######################################################
1.1       stredwic   74: 
1.30      matthew    75: my $Statistics;
                     76: 
1.28      matthew    77: #######################################################
                     78: #######################################################
                     79: 
                     80: =pod
                     81: 
1.31      matthew    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: 
1.32    ! matthew    86: =item $show 'all', 'totals', or 'scores' determines how much data is output
1.31      matthew    87: 
                     88: =cut
                     89: 
                     90: #######################################################
                     91: #######################################################
                     92: my $show_links;
                     93: my $output_mode;
                     94: my $show;
1.28      matthew    95: 
1.31      matthew    96: #######################################################
                     97: #######################################################
                     98: # End of package variable declarations
1.28      matthew    99: 
1.31      matthew   100: =pod
1.28      matthew   101: 
1.31      matthew   102: =back
1.28      matthew   103: 
1.31      matthew   104: =cut
1.28      matthew   105: 
1.31      matthew   106: #######################################################
                    107: #######################################################
1.28      matthew   108: 
1.31      matthew   109: =pod
1.28      matthew   110: 
1.31      matthew   111: =item &BuildStudentAssessmentPage()
1.28      matthew   112: 
1.31      matthew   113: Inputs: 
1.4       stredwic  114: 
1.31      matthew   115: =over 4
1.28      matthew   116: 
                    117: =item $r Apache Request
                    118: 
                    119: =item $c Apache Connection 
                    120: 
                    121: =back
                    122: 
                    123: =cut
                    124: 
                    125: #######################################################
                    126: #######################################################
1.1       stredwic  127: sub BuildStudentAssessmentPage {
1.30      matthew   128:     my ($r,$c)=@_;
                    129:     undef($Statistics);
                    130:     #
1.31      matthew   131:     # Print out the HTML headers for the interface
                    132:     #    This also parses the output mode selector
                    133:     #    This step must always be done.
1.30      matthew   134:     $r->print(&CreateInterface());
1.31      matthew   135:     $r->print('<input type="hidden" name="notfirstrun" value="true" />');
1.7       stredwic  136:     $r->rflush();
1.31      matthew   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:     }
1.30      matthew   163:     #
                    164:     if($c->aborted()) {  return ; }
1.31      matthew   165:     #
                    166:     # Call the initialize routine selected above
                    167:     $initialize->($r);
1.30      matthew   168:     foreach my $student (@Apache::lonstatistics::Students) {
1.31      matthew   169:         if($c->aborted()) { 
                    170:             $finish->($r);
                    171:             return ; 
1.1       stredwic  172:         }
1.31      matthew   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: #######################################################
1.30      matthew   184: 
1.31      matthew   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:         }
1.1       stredwic  204:     }
1.31      matthew   205:     return @Sequences;
1.2       stredwic  206: }
1.30      matthew   207: 
1.31      matthew   208: 
1.28      matthew   209: #######################################################
                    210: #######################################################
                    211: 
                    212: =pod
1.2       stredwic  213: 
1.28      matthew   214: =item &CreateInterface()
1.21      minaeibi  215: 
1.28      matthew   216: Called by &BuildStudentAssessmentPage to create the top part of the
                    217: page which displays the chart.
                    218: 
1.30      matthew   219: Inputs: None
1.28      matthew   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: #######################################################
1.2       stredwic  228: sub CreateInterface {
1.4       stredwic  229:     my $Str = '';
1.30      matthew   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>';
1.31      matthew   236:     $Str .= '<td align="center"><b>Output Format</b></td>';
1.30      matthew   237:     $Str .= '</tr>'."\n";
                    238:     #
1.4       stredwic  239:     $Str .= '<tr><td align="center">'."\n";
1.29      matthew   240:     $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
1.4       stredwic  241:     $Str .= '</td><td align="center">';
1.30      matthew   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);
1.4       stredwic  252:     $Str .= '</td><td>'."\n";
1.30      matthew   253:     $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
                    254:                                               $only_seq_with_assessments);
1.31      matthew   255:     $Str .= '</td><td>'."\n";
                    256:     $Str .= &CreateAndParseOutputSelector();
1.30      matthew   257:     $Str .= '</td></tr>'."\n";
                    258:     $Str .= '</table>'."\n";
1.4       stredwic  259:     return $Str;
1.1       stredwic  260: }
1.30      matthew   261: 
                    262: #######################################################
                    263: #######################################################
                    264: 
                    265: =pod
                    266: 
1.31      matthew   267: =item &CreateAndParseOutputSelector()
1.30      matthew   268: 
                    269: =cut
                    270: 
                    271: #######################################################
                    272: #######################################################
1.32    ! matthew   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: 
1.31      matthew   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:     }
1.32    ! matthew   333:     if ($restriction eq 'totals') {
1.31      matthew   334:         $show = 'totals';
1.32    ! matthew   335:     } elsif ($restriction eq 'scores only') {
        !           336:         $show = 'scores';
1.31      matthew   337:     } else {
                    338:         $show = 'everything';
                    339:     }
                    340:     #
                    341:     # Build the form element
                    342:     $Str = qq/<select size="5" name="$elementname">/;
1.32    ! matthew   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>";
1.31      matthew   347:     }
                    348:     $Str .= "\n</select>";
                    349:     return $Str;
                    350: }
1.30      matthew   351: 
1.28      matthew   352: #######################################################
                    353: #######################################################
1.1       stredwic  354: 
1.28      matthew   355: =pod
                    356: 
1.31      matthew   357: =head2 HTML output routines
1.28      matthew   358: 
1.31      matthew   359: =item &html_initialize($r)
1.28      matthew   360: 
1.31      matthew   361: Create labels for the columns of student data to show.
1.28      matthew   362: 
1.31      matthew   363: =item &html_outputstudent($r,$student)
1.28      matthew   364: 
1.31      matthew   365: Return a line of the chart for a student.
1.28      matthew   366: 
1.31      matthew   367: =item &html_finish($r)
1.28      matthew   368: 
                    369: =cut
                    370: 
                    371: #######################################################
                    372: #######################################################
1.31      matthew   373: {
                    374:     my $padding;
                    375:     my $count;
                    376: 
                    377: sub html_initialize {
                    378:     my ($r) = @_;
1.30      matthew   379:     #
                    380:     $padding = ' 'x3;
1.31      matthew   381:     $count = 1;
1.30      matthew   382:     #
1.31      matthew   383:     my $Str = "<pre>\n";
1.30      matthew   384:     # First, the @StudentData fields need to be listed
1.31      matthew   385:     my @to_show = &get_student_fields_to_show();
1.30      matthew   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
1.31      matthew   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;
1.30      matthew   398:     }
1.31      matthew   399:     $Str .= "total (of shown problems)</pre>\n";
                    400:     $Str .= "<pre>";
                    401:     $r->print($Str);
                    402:     $r->rflush();
                    403:     return;
1.30      matthew   404: }
                    405: 
1.31      matthew   406: sub html_outputstudent {
                    407:     my ($r,$student) = @_;
1.2       stredwic  408:     my $Str = '';
1.30      matthew   409:     # First, the @StudentData fields need to be listed
1.31      matthew   410:     my @to_show = &get_student_fields_to_show();
1.30      matthew   411:     foreach my $field (@to_show) {
                    412:         my $title=$student->{$field};
1.31      matthew   413:         my $base = length($title);
1.30      matthew   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";
1.31      matthew   427:         $r->print($Str);
                    428:         $r->rflush();
                    429:         return;
1.30      matthew   430:     }
                    431:     #
                    432:     # By sequence build up the data
                    433:     my $studentstats;
1.31      matthew   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));
1.32    ! matthew   444:         } elsif ($show eq 'scores') {
        !           445:             $performance = $score;
        !           446:             $performance .= ' 'x($seq->{'width'}-length($performance));
1.31      matthew   447:         } else {
                    448:             # Pad with extra spaces
                    449:             $performance .= ' 'x($seq->{'width'}-$seq_max-
                    450:                                  length($ratio)
                    451:                                  ).$ratio;
1.30      matthew   452:         }
1.31      matthew   453:         #
                    454:         $Str .= $performance.$padding;
                    455:         #
                    456:         $studentstats->{$seq->{'symb'}}->{'score'}= $score;
                    457:         $studentstats->{$seq->{'symb'}}->{'max'}  = $seq_max;
1.30      matthew   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:     }
1.31      matthew   468:     $Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max;
1.30      matthew   469:     $Str .= " \n";
1.31      matthew   470:     $r->print($Str);
                    471:     #
                    472:     $count++;
                    473:     if($count % 5 == 0) {
                    474:         $r->print("</pre><pre>");
                    475:     }
                    476:     #
                    477:     $r->rflush();
                    478:     return;
1.30      matthew   479: }    
1.2       stredwic  480: 
1.31      matthew   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;
1.32    ! matthew   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;
1.31      matthew   518: 
                    519: sub excel_initialize {
                    520:     my ($r) = @_;
                    521:     #
1.32    ! matthew   522:     $filename = '/prtspool/'.
1.31      matthew   523:         $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
                    524:             time.'_'.rand(1000000000).'.xls';
1.32    ! matthew   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)) {
1.31      matthew   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");
1.32    ! matthew   544:         return ;
1.31      matthew   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.
1.32    ! matthew   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();
1.31      matthew   588:     #
                    589:     return;
                    590: }
                    591: 
                    592: sub excel_outputstudent {
                    593:     my ($r,$student) = @_;
1.32    ! matthew   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;
1.31      matthew   655: }
                    656: 
                    657: sub excel_finish {
                    658:     my ($r) = @_;
1.32    ! matthew   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;
1.31      matthew   677: }
                    678: 
                    679: }
1.30      matthew   680: #######################################################
                    681: #######################################################
                    682: 
                    683: =pod
                    684: 
1.31      matthew   685: =head2 CSV output routines
                    686: 
                    687: =item &csv_initialize($r)
                    688: 
                    689: =item &csv_outputstudent($r,$student)
                    690: 
                    691: =item &csv_finish($r)
1.30      matthew   692: 
                    693: =cut
                    694: 
                    695: #######################################################
                    696: #######################################################
1.31      matthew   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: }
1.2       stredwic  712: 
                    713: }
                    714: 
1.28      matthew   715: #######################################################
                    716: #######################################################
                    717: 
1.2       stredwic  718: =pod
                    719: 
1.30      matthew   720: =item &StudentPerformanceOnSequence()
1.2       stredwic  721: 
1.30      matthew   722: Inputs:
1.2       stredwic  723: 
                    724: =over 4
                    725: 
1.30      matthew   726: =item $student
1.28      matthew   727: 
1.30      matthew   728: =item $studentdata Hash ref to all student data
1.2       stredwic  729: 
1.30      matthew   730: =item $seq Hash ref, the sequence we are working on
1.2       stredwic  731: 
1.30      matthew   732: =item $links if defined we will output links to each resource.
1.2       stredwic  733: 
1.28      matthew   734: =back
1.2       stredwic  735: 
                    736: =cut
1.1       stredwic  737: 
1.28      matthew   738: #######################################################
                    739: #######################################################
1.30      matthew   740: sub StudentPerformanceOnSequence {
1.32    ! matthew   741:     my ($student,$studentdata,$seq,$links) = @_;
1.31      matthew   742:     $links = 'no' if (! defined($links));
1.1       stredwic  743:     my $Str = '';
1.30      matthew   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 = ' ';
1.2       stredwic  780:                 }
1.30      matthew   781:             } else {
                    782:                 # Unsolved.  Did they try?
                    783:                 if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
                    784:                     $symbol = '.';
                    785:                 } else {
                    786:                     $symbol = ' ';
1.18      matthew   787:                 }
1.2       stredwic  788:             }
1.30      matthew   789:             #
1.31      matthew   790:             if ($links eq 'yes' && $symbol ne ' ') {
1.30      matthew   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;
1.2       stredwic  798:         }
1.30      matthew   799:         $Str .= $value;
1.17      minaeibi  800:     }
1.30      matthew   801:     return ($Str,$sum,$max);
1.17      minaeibi  802: }
                    803: 
1.28      matthew   804: #######################################################
                    805: #######################################################
1.17      minaeibi  806: sub StudentAverageTotal {
1.21      minaeibi  807:     my ($cache, $students, $sequenceKeys)=@_;
1.23      minaeibi  808:     my $Str = "\n<b>Summary Tables:</b>\n";
1.21      minaeibi  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) {
1.23      minaeibi  817:             $Correct{$sequence} +=
                    818: 	       $cache->{$name.':'.$sequence.':problemsCorrect'};
1.17      minaeibi  819:         }
1.21      minaeibi  820: 	$ProblemsSolved += $cache->{$name.':problemsSolved'};
                    821:         $TotalProblems += $cache->{$name.':totalProblems'};
1.1       stredwic  822:     }
1.25      matthew   823:     if ($StudentCount) { 
1.27      minaeibi  824:         $ProblemsSolved = sprintf( "%.2f", 
                    825:                              $ProblemsSolved/$StudentCount);
1.25      matthew   826:         $TotalProblems /= $StudentCount;
                    827:     } else {
                    828:         $ProblemsSolved = 0;
                    829:         $TotalProblems  = 0;
                    830:     }
1.27      minaeibi  831: 
1.24      minaeibi  832:     $Str .= '<table border=2 cellspacing="1">'."\n";
1.23      minaeibi  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: 
1.24      minaeibi  841:     $Str .= '<table border=2 cellspacing="1">'."\n";
1.23      minaeibi  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'};
1.24      minaeibi  847: 	#my @problems=split(':', $cache->{$S.':problems'});
1.23      minaeibi  848: 	#my $pCount=scalar @problems;
                    849: 	my $pCount=MaxSeqPr($cache,@$students[0],$S);
1.25      matthew   850:         my $crr;
                    851: 	if ($StudentCount) {
                    852:             $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
                    853:         } else {
                    854:             $crr="0.00";
                    855:         }
1.23      minaeibi  856:         $Str .= '<tr><td>'.$title.
                    857:                 '</td><td align=center>'.$pCount.
                    858:                 '</td><td align=center>'.$crr.
                    859:                 '</td></tr>'."\n";
1.10      stredwic  860:     }
1.1       stredwic  861: 
1.23      minaeibi  862:     $Str .= '</table>'."\n";
                    863: 
1.1       stredwic  864:     return $Str;
                    865: }
1.23      minaeibi  866: 
1.28      matthew   867: #######################################################
                    868: #######################################################
1.23      minaeibi  869: 
1.2       stredwic  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: 
1.28      matthew   880: #######################################################
                    881: #######################################################
1.2       stredwic  882: sub CreateLegend {
                    883:     my $Str = "<p><pre>".
1.13      minaeibi  884:               "   1  correct by student in 1 try\n".
                    885:               "   7  correct by student in 7 tries\n".
1.12      minaeibi  886:               "   *  correct by student in more than 9 tries\n".
1.20      minaeibi  887: 	      "   +  correct by hand grading or override\n".
1.12      minaeibi  888:               "   -  incorrect by override\n".
                    889: 	      "   .  incorrect attempted\n".
                    890: 	      "   #  ungraded attempted\n".
1.13      minaeibi  891:               "      not attempted (blank field)\n".
1.12      minaeibi  892: 	      "   x  excused".
1.17      minaeibi  893:               "</pre><p>";
1.2       stredwic  894:     return $Str;
                    895: }
                    896: 
1.28      matthew   897: #######################################################
                    898: #######################################################
                    899: 
1.30      matthew   900: =pod 
1.2       stredwic  901: 
                    902: =back
                    903: 
                    904: =cut
                    905: 
1.28      matthew   906: #######################################################
                    907: #######################################################
1.2       stredwic  908: 
1.28      matthew   909: 1;
1.2       stredwic  910: 
1.1       stredwic  911: __END__

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