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

1.1       stredwic    1: # The LearningOnline Network with CAPA
                      2: #
1.33    ! matthew     3: # $Id: lonstudentassessment.pm,v 1.32 2003/03/03 22:54:05 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
1.33    ! matthew   146: #        $r->print(&OutputDescriptions());
1.31      matthew   147:         return;
                    148:     }
                    149:     #
                    150:     #
                    151:     my $initialize     = \&html_initialize;
                    152:     my $output_student = \&html_outputstudent;
                    153:     my $finish         = \&html_finish;
                    154:     #
                    155:     if ($output_mode eq 'excel') {
                    156:         $initialize     = \&excel_initialize;
                    157:         $output_student = \&excel_outputstudent;
                    158:         $finish         = \&excel_finish;
                    159:     } elsif ($output_mode eq 'csv') {
                    160:         $initialize     = \&csv_initialize;
                    161:         $output_student = \&csv_outputstudent;
                    162:         $finish         = \&csv_finish;
                    163:     }
1.30      matthew   164:     #
                    165:     if($c->aborted()) {  return ; }
1.31      matthew   166:     #
                    167:     # Call the initialize routine selected above
                    168:     $initialize->($r);
1.30      matthew   169:     foreach my $student (@Apache::lonstatistics::Students) {
1.31      matthew   170:         if($c->aborted()) { 
                    171:             $finish->($r);
                    172:             return ; 
1.1       stredwic  173:         }
1.31      matthew   174:         # Call the output_student routine selected above
                    175:         $output_student->($r,$student);
                    176:     }
                    177:     # Call the "finish" routine selected above
                    178:     $finish->($r);
                    179:     #
                    180:     return;
                    181: }
                    182: 
                    183: #######################################################
                    184: #######################################################
1.30      matthew   185: 
1.31      matthew   186: sub get_student_fields_to_show {
                    187:     my @to_show = @Apache::lonstatistics::SelectedStudentData;
                    188:     foreach (@to_show) {
                    189:         if ($_ eq 'all') {
                    190:             @to_show = @Apache::lonstatistics::StudentDataOrder;
                    191:             last;
                    192:         }
                    193:     }
                    194:     return @to_show;
                    195: }
                    196: 
                    197: sub get_sequences_to_show {
                    198:     my @Sequences;
                    199:     foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
                    200:         foreach my $sequence (@Apache::lonstatistics::Sequences) {
                    201:             next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all');
                    202:             next if ($sequence->{'num_assess'} < 1);
                    203:             push (@Sequences,$sequence);
                    204:         }
1.1       stredwic  205:     }
1.31      matthew   206:     return @Sequences;
1.2       stredwic  207: }
1.30      matthew   208: 
1.31      matthew   209: 
1.28      matthew   210: #######################################################
                    211: #######################################################
                    212: 
                    213: =pod
1.2       stredwic  214: 
1.28      matthew   215: =item &CreateInterface()
1.21      minaeibi  216: 
1.28      matthew   217: Called by &BuildStudentAssessmentPage to create the top part of the
                    218: page which displays the chart.
                    219: 
1.30      matthew   220: Inputs: None
1.28      matthew   221: 
                    222: Returns:  A string containing the HTML for the headers and top table for 
                    223: the chart page.
                    224: 
                    225: =cut
                    226: 
                    227: #######################################################
                    228: #######################################################
1.2       stredwic  229: sub CreateInterface {
1.4       stredwic  230:     my $Str = '';
1.30      matthew   231: #    $Str .= &CreateLegend();
                    232:     $Str .= '<table cellspacing="5">'."\n";
                    233:     $Str .= '<tr>';
                    234:     $Str .= '<td align="center"><b>Sections</b></td>';
                    235:     $Str .= '<td align="center"><b>Student Data</b></td>';
                    236:     $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
1.31      matthew   237:     $Str .= '<td align="center"><b>Output Format</b></td>';
1.30      matthew   238:     $Str .= '</tr>'."\n";
                    239:     #
1.4       stredwic  240:     $Str .= '<tr><td align="center">'."\n";
1.29      matthew   241:     $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
1.4       stredwic  242:     $Str .= '</td><td align="center">';
1.30      matthew   243:     my $only_seq_with_assessments = sub { 
                    244:         my $s=shift;
                    245:         if ($s->{'num_assess'} < 1) { 
                    246:             return 0;
                    247:         } else { 
                    248:             return 1;
                    249:         }
                    250:     };
                    251:     $Str .= &Apache::lonstatistics::StudentDataSelect('StudentData','multiple',
                    252:                                                       5,undef);
1.4       stredwic  253:     $Str .= '</td><td>'."\n";
1.30      matthew   254:     $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
                    255:                                               $only_seq_with_assessments);
1.31      matthew   256:     $Str .= '</td><td>'."\n";
                    257:     $Str .= &CreateAndParseOutputSelector();
1.30      matthew   258:     $Str .= '</td></tr>'."\n";
                    259:     $Str .= '</table>'."\n";
1.4       stredwic  260:     return $Str;
1.1       stredwic  261: }
1.30      matthew   262: 
                    263: #######################################################
                    264: #######################################################
                    265: 
                    266: =pod
                    267: 
1.31      matthew   268: =item &CreateAndParseOutputSelector()
1.30      matthew   269: 
                    270: =cut
                    271: 
                    272: #######################################################
                    273: #######################################################
1.32      matthew   274: my @OutputOptions = 
                    275:     ({ name  => 'HTML, with links',
                    276:        value => 'html, with links',
1.33    ! matthew   277:        description => 'Output HTML with each symbol linked to the problem '.
        !           278: 	   'which generated it.'},
1.32      matthew   279:      { name  => 'HTML, without links',
                    280:        value => 'html, without links',
1.33    ! matthew   281:        description => 'Output HTML.  By not including links, the size of the'.
        !           282: 	   ' web page is greatly reduced.  If your browser crashes on the '.
        !           283: 	   'full display, try this.'},
        !           284:      { name  => 'HTML, scores only',
        !           285:        value => 'html, scores only',
        !           286:        description => 'Output HTML, only showing the total number of correct'.
        !           287: 	   ' problems (or problem parts) and not the maximum possible for '.
        !           288: 	   'each student'},
1.32      matthew   289:      { name  => 'HTML, totals',
                    290:        value => 'html, totals',
1.33    ! matthew   291:        description => 'Output HTML, but only the summary statistics for each'.
        !           292: 	   ' sequence selected.'},
        !           293:      { name  => 'Excel, scores only',
        !           294:        value => 'excel, scores only',
        !           295:        description => 'Output an Excel file (compatable with Excel 95), '.
        !           296: 	   'with a single column for each sequence showing the students '.
        !           297: 	   'score.'},
        !           298: #     { name  => 'Excel, everything',
        !           299: #       value => 'excel, everything',
        !           300: #       description => 'Output an Excel file (compatable with Excel 95), '.
        !           301: #	   'with a seperate worksheet for each sequence you have selected '.
        !           302: #           'the data for each problem part '.
        !           303: #           '(number of tries, status, points awarded) '.
        !           304: #           'will be listed.'},
1.32      matthew   305:      { name  => 'Excel, totals',
                    306:        value => 'excel, totals',
1.33    ! matthew   307:        description => 'Output an Excel file (compatable with Excel 95), '.
        !           308: 	   'with two columns for each sequence, the students score on the '.
        !           309: 	   'sequence and the students maximum possible on the sequence'},
        !           310:      { name  => 'CSV, everything',
        !           311:        value => 'csv, everything',
1.32      matthew   312:        description => ''},
1.33    ! matthew   313:      { name  => 'CSV, scores only',
        !           314:        value => 'csv, scores only',
1.32      matthew   315:        description => ''},
                    316:      { name  => 'CSV, totals',
                    317:        value => 'csv, totals',
                    318:        description => ''},
                    319:      );
                    320: 
1.33    ! matthew   321: sub OutputDescriptions {
        !           322:     my $Str = '';
        !           323:     $Str .= "<h2>Output Modes</h2>\n";
        !           324:     $Str .= "<dl>\n";
        !           325:     foreach my $outputmode (@OutputOptions) {
        !           326: 	$Str .="    <dt>".$outputmode->{'name'}."</dt>\n";
        !           327: 	$Str .="        <dd>".$outputmode->{'description'}."</dd>\n";
        !           328:     }
        !           329:     $Str .= "</dl>\n";
        !           330:     return $Str;
        !           331: }
        !           332: 
1.31      matthew   333: sub CreateAndParseOutputSelector {
                    334:     my $Str = '';
                    335:     my $elementname = 'outputmode';
                    336:     #
                    337:     # Format for output options is 'mode, restrictions';
                    338:     my $selected = 'html, with links';
                    339:     if (exists($ENV{'form.'.$elementname})) {
                    340:         if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
                    341:             $selected = $ENV{'form.'.$elementname}->[0];
                    342:         } else {
                    343:             $selected = $ENV{'form.'.$elementname};
                    344:         }
                    345:     }
                    346:     #
                    347:     # Set package variables describing output mode
                    348:     $show_links  = 'no';
                    349:     $output_mode = 'html';
                    350:     $show        = 'all';
                    351:     my ($mode,$restriction) = split(',',$selected);
                    352:     $restriction =~ s/^\s*//;
                    353:     if ($mode =~ /^(html|excel|csv)$/) {
                    354:         $output_mode = $mode;
                    355:     } else {
                    356:         $output_mode = 'html';
                    357:     }
                    358:     if ($restriction eq 'with links') {
                    359:         $show_links = 'yes';
                    360:     } else {
                    361:         $show_links = 'no';
                    362:     }
1.32      matthew   363:     if ($restriction eq 'totals') {
1.31      matthew   364:         $show = 'totals';
1.32      matthew   365:     } elsif ($restriction eq 'scores only') {
                    366:         $show = 'scores';
1.31      matthew   367:     } else {
                    368:         $show = 'everything';
                    369:     }
                    370:     #
                    371:     # Build the form element
                    372:     $Str = qq/<select size="5" name="$elementname">/;
1.32      matthew   373:     foreach my $option (@OutputOptions) {
                    374:         $Str .= "\n".'    <option value="'.$option->{'value'}.'"';
                    375:         $Str .= " selected " if ($option->{'value'} eq $selected);
                    376:         $Str .= ">".$option->{'name'}."<\/option>";
1.31      matthew   377:     }
                    378:     $Str .= "\n</select>";
                    379:     return $Str;
                    380: }
1.30      matthew   381: 
1.28      matthew   382: #######################################################
                    383: #######################################################
1.1       stredwic  384: 
1.28      matthew   385: =pod
                    386: 
1.31      matthew   387: =head2 HTML output routines
1.28      matthew   388: 
1.31      matthew   389: =item &html_initialize($r)
1.28      matthew   390: 
1.31      matthew   391: Create labels for the columns of student data to show.
1.28      matthew   392: 
1.31      matthew   393: =item &html_outputstudent($r,$student)
1.28      matthew   394: 
1.31      matthew   395: Return a line of the chart for a student.
1.28      matthew   396: 
1.31      matthew   397: =item &html_finish($r)
1.28      matthew   398: 
                    399: =cut
                    400: 
                    401: #######################################################
                    402: #######################################################
1.31      matthew   403: {
                    404:     my $padding;
                    405:     my $count;
                    406: 
                    407: sub html_initialize {
                    408:     my ($r) = @_;
1.30      matthew   409:     #
                    410:     $padding = ' 'x3;
1.31      matthew   411:     $count = 1;
1.30      matthew   412:     #
1.31      matthew   413:     my $Str = "<pre>\n";
1.30      matthew   414:     # First, the @StudentData fields need to be listed
1.31      matthew   415:     my @to_show = &get_student_fields_to_show();
1.30      matthew   416:     foreach my $field (@to_show) {
                    417:         my $title=$Apache::lonstatistics::StudentData{$field}->{'title'};
                    418:         my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'};
                    419:         my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
                    420:         $Str .= $title.' 'x($width-$base).$padding;
                    421:     }
                    422:     # Now the selected sequences need to be listed
1.31      matthew   423:     foreach my $sequence (&get_sequences_to_show) {
                    424:         my $title = $sequence->{'title'};
                    425:         my $base  = $sequence->{'base_width'};
                    426:         my $width = $sequence->{'width'};
                    427:         $Str .= $title.' 'x($width-$base).$padding;
1.30      matthew   428:     }
1.31      matthew   429:     $Str .= "total (of shown problems)</pre>\n";
                    430:     $Str .= "<pre>";
                    431:     $r->print($Str);
                    432:     $r->rflush();
                    433:     return;
1.30      matthew   434: }
                    435: 
1.31      matthew   436: sub html_outputstudent {
                    437:     my ($r,$student) = @_;
1.2       stredwic  438:     my $Str = '';
1.30      matthew   439:     # First, the @StudentData fields need to be listed
1.31      matthew   440:     my @to_show = &get_student_fields_to_show();
1.30      matthew   441:     foreach my $field (@to_show) {
                    442:         my $title=$student->{$field};
1.31      matthew   443:         my $base = length($title);
1.30      matthew   444:         my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
                    445:         $Str .= $title.' 'x($width-$base).$padding;
                    446:     }
                    447:     # Get ALL the students data
                    448:     my %StudentsData;
                    449:     my @tmp = &Apache::loncoursedata::get_current_state
                    450:         ($student->{'username'},$student->{'domain'},undef,
                    451:          $ENV{'request.course.id'});
                    452:     if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
                    453:         %StudentsData = @tmp;
                    454:     }
                    455:     if (scalar(@tmp) < 1) {
                    456:         $Str .= '<font color="blue">No Course Data</font>'."\n";
1.31      matthew   457:         $r->print($Str);
                    458:         $r->rflush();
                    459:         return;
1.30      matthew   460:     }
                    461:     #
                    462:     # By sequence build up the data
                    463:     my $studentstats;
1.31      matthew   464:     my $PerformanceStr = '';
                    465:     foreach my $seq (&get_sequences_to_show) {
                    466:         my ($performance,$score,$seq_max) =
                    467:             &StudentPerformanceOnSequence($student,\%StudentsData,
                    468:                                           $seq,$show_links);
                    469:         my $ratio = $score.'/'.$seq_max;
                    470:         #
                    471:         if ($show eq 'totals') {
                    472:             $performance = ' 'x(length($seq_max)-length($score)).$ratio;
                    473:             $performance .= ' 'x($seq->{'width'}-length($performance));
1.32      matthew   474:         } elsif ($show eq 'scores') {
                    475:             $performance = $score;
                    476:             $performance .= ' 'x($seq->{'width'}-length($performance));
1.31      matthew   477:         } else {
                    478:             # Pad with extra spaces
                    479:             $performance .= ' 'x($seq->{'width'}-$seq_max-
                    480:                                  length($ratio)
                    481:                                  ).$ratio;
1.30      matthew   482:         }
1.31      matthew   483:         #
                    484:         $Str .= $performance.$padding;
                    485:         #
                    486:         $studentstats->{$seq->{'symb'}}->{'score'}= $score;
                    487:         $studentstats->{$seq->{'symb'}}->{'max'}  = $seq_max;
1.30      matthew   488:     }
                    489:     #
                    490:     # Total it up and store the statistics info.
                    491:     my ($score,$max) = (0,0);
                    492:     while (my ($symb,$seq_stats) = each (%{$studentstats})) {
                    493:         $Statistics->{$symb}->{'score'} += $seq_stats->{'score'};
                    494:         $Statistics->{$symb}->{'max'}   += $seq_stats->{'max'};
                    495:         $score += $seq_stats->{'score'};
                    496:         $max   += $seq_stats->{'max'};
                    497:     }
1.31      matthew   498:     $Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max;
1.30      matthew   499:     $Str .= " \n";
1.31      matthew   500:     $r->print($Str);
                    501:     #
                    502:     $count++;
                    503:     if($count % 5 == 0) {
                    504:         $r->print("</pre><pre>");
                    505:     }
                    506:     #
                    507:     $r->rflush();
                    508:     return;
1.30      matthew   509: }    
1.2       stredwic  510: 
1.31      matthew   511: sub html_finish {
                    512:     my ($r) = @_;
                    513:     $r->print("</pre>\n"); 
                    514:     $r->rflush();
                    515:     return;
                    516: }
                    517: 
                    518: }
                    519: 
                    520: #######################################################
                    521: #######################################################
                    522: 
                    523: =pod
                    524: 
                    525: =head2 EXCEL subroutines
                    526: 
                    527: =item &excel_initialize($r)
                    528: 
                    529: =item &excel_outputstudent($r,$student)
                    530: 
                    531: =item &excel_finish($r)
                    532: 
                    533: =cut
                    534: 
                    535: #######################################################
                    536: #######################################################
                    537: {
                    538: 
                    539: my $excel_sheet;
1.32      matthew   540: my $excel_workbook;
                    541: 
                    542: my $filename;
                    543: my $rows_output;
                    544: my $cols_output;
                    545: 
                    546: my $num_students;
                    547: my $start_time;
1.31      matthew   548: 
                    549: sub excel_initialize {
                    550:     my ($r) = @_;
                    551:     #
1.32      matthew   552:     $filename = '/prtspool/'.
1.31      matthew   553:         $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
                    554:             time.'_'.rand(1000000000).'.xls';
1.32      matthew   555:     #
                    556:     $excel_workbook = undef;
                    557:     $excel_sheet = undef;
                    558:     #
                    559:     $rows_output = 0;
                    560:     $cols_output = 0;
                    561:     #
                    562:     $num_students = 0;
                    563:     $start_time   = time;
                    564:     #
                    565:     # Create sheet
                    566:     $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
                    567:     #
                    568:     # Check for errors
                    569:     if (! defined($excel_workbook)) {
1.31      matthew   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");
1.32      matthew   574:         return ;
1.31      matthew   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.
1.32      matthew   581:     $excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
                    582:     #
                    583:     # Add a worksheet
1.33    ! matthew   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);
1.32      matthew   589:     #
                    590:     # Add the student headers
                    591:     foreach my $field (&get_student_fields_to_show()) {
                    592:         $excel_sheet->write(1,$cols_output++,$field);
                    593:     }
                    594:     #
                    595:     # Add the Sequence Headers
                    596:     foreach my $seq (&get_sequences_to_show) {
                    597:         $excel_sheet->write(0,$cols_output,$seq->{'title'});
                    598:         if ($show eq 'totals') {
                    599:             $excel_sheet->write(1,$cols_output,'score');
                    600:             $excel_sheet->write(1,$cols_output+1,'maximum');
                    601:             $cols_output += 2;
                    602:         } else {
                    603:             $cols_output++;
                    604:         }
                    605:     }
                    606:     #
                    607:     # Bookkeeping
                    608:     if ($show eq 'totals') {
                    609:         $rows_output = 2;
                    610:     } else {
                    611:         $rows_output = 1;
                    612:     }
                    613:     #
                    614:     # Let the user know what we are doing
                    615:     my $studentcount = scalar(@Apache::lonstatistics::Students); 
                    616:     $r->print("<h1>Compiling Excel spreadsheet for ".
                    617:               $studentcount.' student');
                    618:     $r->print('s') if ($studentcount > 1);
                    619:     $r->print("</h1>\n");
                    620:     $r->rflush();
1.31      matthew   621:     #
                    622:     return;
                    623: }
                    624: 
                    625: sub excel_outputstudent {
                    626:     my ($r,$student) = @_;
1.32      matthew   627:     return if (! defined($excel_sheet));
                    628:     $cols_output=0;
                    629:     #
                    630:     # Write out student data
                    631:     my @to_show = &get_student_fields_to_show();
                    632:     foreach my $field (@to_show) {
                    633:         $excel_sheet->write($rows_output,$cols_output++,$student->{$field});
                    634:     }
                    635:     #
                    636:     # Get student assessment data
                    637:     my %StudentsData;
                    638:     my @tmp = &Apache::loncoursedata::get_current_state($student->{'username'},
                    639:                                                         $student->{'domain'},
                    640:                                                         undef,
                    641:                                                    $ENV{'request.course.id'});
                    642:     if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
                    643:         %StudentsData = @tmp;
                    644:     }
                    645:     #
                    646:     # Write out sequence scores and totals data
                    647:     foreach my $seq (&get_sequences_to_show) {
                    648:         my ($performance,$score,$seq_max) =
                    649:             &StudentPerformanceOnSequence($student,\%StudentsData,
                    650:                                           $seq,'no');
                    651:         if ($show eq 'totals' || $show eq 'scores') {
                    652:             $excel_sheet->write($rows_output,$cols_output++,$score);
                    653:         }
                    654:         if ($show eq 'totals') {
                    655:             $excel_sheet->write($rows_output,$cols_output++,$seq_max);
                    656:         }
                    657:     }
                    658:     #
                    659:     # Bookkeeping
                    660:     $rows_output++; 
                    661:     $cols_output=0;
                    662:     #
                    663:     # Time estimate
                    664:     $num_students++;
                    665:     if ($num_students % 10 == 0) {
                    666:         my $time_est = (time - $start_time)/$num_students * 
                    667:             (scalar(@Apache::lonstatistics::Students)-$num_students);
                    668:         $time_est = int($time_est);
                    669:         if (int ($time_est/60) > 0) {
                    670:             my $min = int($time_est/60);
                    671:             my $sec = $time_est % 60;
                    672:             $time_est = $min.' minutes';
                    673:             if ($sec > 1) {
                    674:                 $time_est.= ', '.$sec.' seconds';
                    675:             } elsif ($sec > 0) {
                    676:                 $time_est.= ', '.$sec.' second';
                    677:             }
                    678:         } else {
                    679:             $time_est .= ' seconds';
                    680:         }
                    681:         $r->print($num_students.' out of '.
                    682:                   (scalar(@Apache::lonstatistics::Students)).
                    683:                   " students processed.  ".
                    684:                   $time_est." remain.  <br />\n");
                    685:         $r->rflush();
                    686:     }
                    687:     return;
1.31      matthew   688: }
                    689: 
                    690: sub excel_finish {
                    691:     my ($r) = @_;
1.32      matthew   692:     return if (! defined($excel_sheet));
                    693:     #
                    694:     # Write the excel file
                    695:     $excel_workbook->close();
                    696:     my $c = $r->connection();
                    697:     #
                    698:     return if($c->aborted());
                    699:     #
                    700:     # Tell the user where to get their excel file
                    701:     $r->print('<br /><br />'.
                    702:               '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
                    703:     my $total_time = time - $start_time;
                    704:     if (int ($total_time / 60) > 0) {
                    705:         $total_time = int($total_time/60).' minutes, '.($total_time % 60);
                    706:     }
                    707:     $r->print('<br />'.$total_time.' seconds total');
                    708:     $r->rflush();
                    709:     return;
1.31      matthew   710: }
                    711: 
                    712: }
1.30      matthew   713: #######################################################
                    714: #######################################################
                    715: 
                    716: =pod
                    717: 
1.31      matthew   718: =head2 CSV output routines
                    719: 
                    720: =item &csv_initialize($r)
                    721: 
                    722: =item &csv_outputstudent($r,$student)
                    723: 
                    724: =item &csv_finish($r)
1.30      matthew   725: 
                    726: =cut
                    727: 
                    728: #######################################################
                    729: #######################################################
1.31      matthew   730: {
                    731: 
                    732: sub csv_initialize{
                    733:     my ($r) = @_;
                    734:     $r->print("<h1>Not implemented yet</h1>");
                    735:     return;
                    736: }
                    737: 
                    738: sub csv_outputstudent {
                    739:     my ($r,$student) = @_;
                    740: }
                    741: 
                    742: sub csv_finish {
                    743:     my ($r) = @_;
                    744: }
1.2       stredwic  745: 
                    746: }
                    747: 
1.28      matthew   748: #######################################################
                    749: #######################################################
                    750: 
1.2       stredwic  751: =pod
                    752: 
1.30      matthew   753: =item &StudentPerformanceOnSequence()
1.2       stredwic  754: 
1.30      matthew   755: Inputs:
1.2       stredwic  756: 
                    757: =over 4
                    758: 
1.30      matthew   759: =item $student
1.28      matthew   760: 
1.30      matthew   761: =item $studentdata Hash ref to all student data
1.2       stredwic  762: 
1.30      matthew   763: =item $seq Hash ref, the sequence we are working on
1.2       stredwic  764: 
1.30      matthew   765: =item $links if defined we will output links to each resource.
1.2       stredwic  766: 
1.28      matthew   767: =back
1.2       stredwic  768: 
                    769: =cut
1.1       stredwic  770: 
1.28      matthew   771: #######################################################
                    772: #######################################################
1.30      matthew   773: sub StudentPerformanceOnSequence {
1.32      matthew   774:     my ($student,$studentdata,$seq,$links) = @_;
1.31      matthew   775:     $links = 'no' if (! defined($links));
1.1       stredwic  776:     my $Str = '';
1.30      matthew   777:     my ($sum,$max) = (0,0);
                    778:     foreach my $resource (@{$seq->{'contents'}}) {
                    779:         next if ($resource->{'type'} ne 'assessment');
                    780:         my $resource_data = $studentdata->{$resource->{'symb'}};
                    781:         my $value = '';
                    782:         foreach my $partnum (@{$resource->{'parts'}}) {
                    783:             $max++;
                    784:             my $symbol = ' '; # default to space
                    785:             #
                    786:             if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {
                    787:                 my $status = $resource_data->{'resource.'.$partnum.'.solved'};
                    788:                 if ($status eq 'correct_by_override') {
                    789:                     $symbol = '+';
                    790:                     $sum++;
                    791:                 } elsif ($status eq 'incorrect_by_override') {
                    792:                     $symbol = '-';
                    793:                 } elsif ($status eq 'ungraded_attempted') {
                    794:                     $symbol = '#';
                    795:                 } elsif ($status eq 'incorrect_attempted')  {
                    796:                     $symbol = '.';
                    797:                 } elsif ($status eq 'excused') {
                    798:                     $symbol = 'x';
                    799:                     $max--;
                    800:                 } elsif ($status eq 'correct_by_student' &&
                    801:                     exists($resource_data->{'resource.'.$partnum.'.tries'})){
                    802:                     my $num = $resource_data->{'resource.'.$partnum.'.tries'};
                    803:                     if ($num > 9) {
                    804:                         $symbol = '*';
                    805:                     } elsif ($num > 0) {
                    806:                         $symbol = $num;
                    807:                     } else {
                    808:                         $symbol = ' ';
                    809:                     }
                    810:                     $sum++;
                    811:                 } else {
                    812:                     $symbol = ' ';
1.2       stredwic  813:                 }
1.30      matthew   814:             } else {
                    815:                 # Unsolved.  Did they try?
                    816:                 if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
                    817:                     $symbol = '.';
                    818:                 } else {
                    819:                     $symbol = ' ';
1.18      matthew   820:                 }
1.2       stredwic  821:             }
1.30      matthew   822:             #
1.31      matthew   823:             if ($links eq 'yes' && $symbol ne ' ') {
1.30      matthew   824:                 $symbol = '<a href="/adm/grades'.
                    825:                     '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
                    826:                         '&student='.$student->{'username'}.
                    827:                             '&domain='.$student->{'domain'}.
                    828:                                 '&command=submission">'.$symbol.'</a>';
                    829:             }
                    830:             $value .= $symbol;
1.2       stredwic  831:         }
1.30      matthew   832:         $Str .= $value;
1.17      minaeibi  833:     }
1.30      matthew   834:     return ($Str,$sum,$max);
1.17      minaeibi  835: }
                    836: 
1.28      matthew   837: #######################################################
                    838: #######################################################
1.17      minaeibi  839: sub StudentAverageTotal {
1.21      minaeibi  840:     my ($cache, $students, $sequenceKeys)=@_;
1.23      minaeibi  841:     my $Str = "\n<b>Summary Tables:</b>\n";
1.21      minaeibi  842:     my %Correct = ();
                    843:     my $ProblemsSolved = 0;
                    844:     my $TotalProblems = 0;
                    845:     my $StudentCount = 0;
                    846: 
                    847:     foreach my $name (@$students) {
                    848:         $StudentCount++;
                    849:         foreach my $sequence (@$sequenceKeys) {
1.23      minaeibi  850:             $Correct{$sequence} +=
                    851: 	       $cache->{$name.':'.$sequence.':problemsCorrect'};
1.17      minaeibi  852:         }
1.21      minaeibi  853: 	$ProblemsSolved += $cache->{$name.':problemsSolved'};
                    854:         $TotalProblems += $cache->{$name.':totalProblems'};
1.1       stredwic  855:     }
1.25      matthew   856:     if ($StudentCount) { 
1.27      minaeibi  857:         $ProblemsSolved = sprintf( "%.2f", 
                    858:                              $ProblemsSolved/$StudentCount);
1.25      matthew   859:         $TotalProblems /= $StudentCount;
                    860:     } else {
                    861:         $ProblemsSolved = 0;
                    862:         $TotalProblems  = 0;
                    863:     }
1.27      minaeibi  864: 
1.24      minaeibi  865:     $Str .= '<table border=2 cellspacing="1">'."\n";
1.23      minaeibi  866:     $Str .= '<tr><td><b>Students Count</b></td><td><b>'.
                    867:             $StudentCount.'</b></td></tr>'."\n";
                    868:     $Str .= '<tr><td><b>Total Problems</b></td><td><b>'.
                    869:             $TotalProblems.'</b></td></tr>'."\n";
                    870:     $Str .= '<tr><td><b>Average Correct</b></td><td><b>'.
                    871:             $ProblemsSolved.'</b></td></tr>'."\n";
                    872:     $Str .= '</table>'."\n";
                    873: 
1.24      minaeibi  874:     $Str .= '<table border=2 cellspacing="1">'."\n";
1.23      minaeibi  875:     $Str .= '<tr><th>Title</th><th>Total Problems</th>'.
                    876:             '<th>Average Correct</th></tr>'."\n";
                    877:     foreach my $S(@$sequenceKeys) {
                    878:         my $title=$cache->{$S.':title'};
                    879: 	#$Str .= $cache->{$S.':problems'};
1.24      minaeibi  880: 	#my @problems=split(':', $cache->{$S.':problems'});
1.23      minaeibi  881: 	#my $pCount=scalar @problems;
                    882: 	my $pCount=MaxSeqPr($cache,@$students[0],$S);
1.25      matthew   883:         my $crr;
                    884: 	if ($StudentCount) {
                    885:             $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
                    886:         } else {
                    887:             $crr="0.00";
                    888:         }
1.23      minaeibi  889:         $Str .= '<tr><td>'.$title.
                    890:                 '</td><td align=center>'.$pCount.
                    891:                 '</td><td align=center>'.$crr.
                    892:                 '</td></tr>'."\n";
1.10      stredwic  893:     }
1.1       stredwic  894: 
1.23      minaeibi  895:     $Str .= '</table>'."\n";
                    896: 
1.1       stredwic  897:     return $Str;
                    898: }
1.23      minaeibi  899: 
1.28      matthew   900: #######################################################
                    901: #######################################################
1.23      minaeibi  902: 
1.2       stredwic  903: =pod
                    904: 
                    905: =item &CreateLegend()
                    906: 
                    907: This function returns a formatted string containing the legend for the
                    908: chart.  The legend describes the symbols used to represent grades for
                    909: problems.
                    910: 
                    911: =cut
                    912: 
1.28      matthew   913: #######################################################
                    914: #######################################################
1.2       stredwic  915: sub CreateLegend {
                    916:     my $Str = "<p><pre>".
1.13      minaeibi  917:               "   1  correct by student in 1 try\n".
                    918:               "   7  correct by student in 7 tries\n".
1.12      minaeibi  919:               "   *  correct by student in more than 9 tries\n".
1.20      minaeibi  920: 	      "   +  correct by hand grading or override\n".
1.12      minaeibi  921:               "   -  incorrect by override\n".
                    922: 	      "   .  incorrect attempted\n".
                    923: 	      "   #  ungraded attempted\n".
1.13      minaeibi  924:               "      not attempted (blank field)\n".
1.12      minaeibi  925: 	      "   x  excused".
1.17      minaeibi  926:               "</pre><p>";
1.2       stredwic  927:     return $Str;
                    928: }
                    929: 
1.28      matthew   930: #######################################################
                    931: #######################################################
                    932: 
1.30      matthew   933: =pod 
1.2       stredwic  934: 
                    935: =back
                    936: 
                    937: =cut
                    938: 
1.28      matthew   939: #######################################################
                    940: #######################################################
1.2       stredwic  941: 
1.28      matthew   942: 1;
1.2       stredwic  943: 
1.1       stredwic  944: __END__

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