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

1.1       stredwic    1: # The LearningOnline Network with CAPA
                      2: #
1.34    ! matthew     3: # $Id: lonstudentassessment.pm,v 1.33 2003/03/04 14:21:36 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:     #
1.34    ! matthew   590:     # Put the course description in the header
        !           591:     $excel_sheet->write($rows_output,$cols_output++,
        !           592:                    $ENV{'course.'.$ENV{'request.course.id'}.'.description'});
        !           593:     $cols_output += 3;
        !           594:     #
        !           595:     # Put a description of the sections listed
        !           596:     my $sectionstring = '';
        !           597:     my @Sections = @Apache::lonstatistics::SelectedSections;
        !           598:     if (scalar(@Sections) > 1) {
        !           599:         if (scalar(@Sections) > 2) {
        !           600:             my $last = pop(@Sections);
        !           601:             $sectionstring = "Sections ".join(', ',@Sections).', and '.$last;
        !           602:         } else {
        !           603:             $sectionstring = "Sections ".join(' and ',@Sections);
        !           604:         }
        !           605:     } else {
        !           606:         if ($Sections[0] eq 'all') {
        !           607:             $sectionstring = "All sections";
        !           608:         } else {
        !           609:             $sectionstring = "Section ".$Sections[0];
        !           610:         }
        !           611:     }
        !           612:     $excel_sheet->write($rows_output,$cols_output++,$sectionstring);
        !           613:     $cols_output += scalar(@Sections);
        !           614:     #
        !           615:     # Put the date in there too
        !           616:     $excel_sheet->write($rows_output,$cols_output++,
        !           617:                         'Compiled on '.localtime(time));
        !           618:     #
        !           619:     $rows_output++;
        !           620:     #
1.32      matthew   621:     # Add the student headers
1.34    ! matthew   622:     $cols_output = 0;
1.32      matthew   623:     foreach my $field (&get_student_fields_to_show()) {
1.34    ! matthew   624:         $excel_sheet->write($rows_output,$cols_output++,$field);
1.32      matthew   625:     }
                    626:     #
                    627:     # Add the Sequence Headers
                    628:     foreach my $seq (&get_sequences_to_show) {
1.34    ! matthew   629:         $excel_sheet->write($rows_output,$cols_output,$seq->{'title'});
1.32      matthew   630:         if ($show eq 'totals') {
1.34    ! matthew   631:             $excel_sheet->write($rows_output+1,$cols_output,'score');
        !           632:             $excel_sheet->write($rows_output+1,$cols_output+1,'maximum');
1.32      matthew   633:             $cols_output += 2;
                    634:         } else {
                    635:             $cols_output++;
                    636:         }
                    637:     }
                    638:     #
                    639:     # Bookkeeping
                    640:     if ($show eq 'totals') {
1.34    ! matthew   641:         $rows_output += 2;
1.32      matthew   642:     } else {
1.34    ! matthew   643:         $rows_output += 1;
1.32      matthew   644:     }
                    645:     #
                    646:     # Let the user know what we are doing
                    647:     my $studentcount = scalar(@Apache::lonstatistics::Students); 
                    648:     $r->print("<h1>Compiling Excel spreadsheet for ".
                    649:               $studentcount.' student');
                    650:     $r->print('s') if ($studentcount > 1);
                    651:     $r->print("</h1>\n");
                    652:     $r->rflush();
1.31      matthew   653:     #
                    654:     return;
                    655: }
                    656: 
                    657: sub excel_outputstudent {
                    658:     my ($r,$student) = @_;
1.32      matthew   659:     return if (! defined($excel_sheet));
                    660:     $cols_output=0;
                    661:     #
                    662:     # Write out student data
                    663:     my @to_show = &get_student_fields_to_show();
                    664:     foreach my $field (@to_show) {
                    665:         $excel_sheet->write($rows_output,$cols_output++,$student->{$field});
                    666:     }
                    667:     #
                    668:     # Get student assessment data
                    669:     my %StudentsData;
                    670:     my @tmp = &Apache::loncoursedata::get_current_state($student->{'username'},
                    671:                                                         $student->{'domain'},
                    672:                                                         undef,
                    673:                                                    $ENV{'request.course.id'});
                    674:     if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
                    675:         %StudentsData = @tmp;
                    676:     }
                    677:     #
                    678:     # Write out sequence scores and totals data
                    679:     foreach my $seq (&get_sequences_to_show) {
                    680:         my ($performance,$score,$seq_max) =
                    681:             &StudentPerformanceOnSequence($student,\%StudentsData,
                    682:                                           $seq,'no');
                    683:         if ($show eq 'totals' || $show eq 'scores') {
                    684:             $excel_sheet->write($rows_output,$cols_output++,$score);
                    685:         }
                    686:         if ($show eq 'totals') {
                    687:             $excel_sheet->write($rows_output,$cols_output++,$seq_max);
                    688:         }
                    689:     }
                    690:     #
                    691:     # Bookkeeping
                    692:     $rows_output++; 
                    693:     $cols_output=0;
                    694:     #
                    695:     # Time estimate
                    696:     $num_students++;
                    697:     if ($num_students % 10 == 0) {
                    698:         my $time_est = (time - $start_time)/$num_students * 
                    699:             (scalar(@Apache::lonstatistics::Students)-$num_students);
                    700:         $time_est = int($time_est);
                    701:         if (int ($time_est/60) > 0) {
                    702:             my $min = int($time_est/60);
                    703:             my $sec = $time_est % 60;
                    704:             $time_est = $min.' minutes';
                    705:             if ($sec > 1) {
                    706:                 $time_est.= ', '.$sec.' seconds';
                    707:             } elsif ($sec > 0) {
                    708:                 $time_est.= ', '.$sec.' second';
                    709:             }
                    710:         } else {
                    711:             $time_est .= ' seconds';
                    712:         }
                    713:         $r->print($num_students.' out of '.
                    714:                   (scalar(@Apache::lonstatistics::Students)).
                    715:                   " students processed.  ".
1.34    ! matthew   716:                   $time_est." remain.  Elapsed: ".(time - $start_time).
        !           717:                   "<br />\n");
1.32      matthew   718:         $r->rflush();
                    719:     }
                    720:     return;
1.31      matthew   721: }
                    722: 
                    723: sub excel_finish {
                    724:     my ($r) = @_;
1.32      matthew   725:     return if (! defined($excel_sheet));
                    726:     #
                    727:     # Write the excel file
                    728:     $excel_workbook->close();
                    729:     my $c = $r->connection();
                    730:     #
                    731:     return if($c->aborted());
                    732:     #
                    733:     # Tell the user where to get their excel file
                    734:     $r->print('<br /><br />'.
                    735:               '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
                    736:     my $total_time = time - $start_time;
                    737:     if (int ($total_time / 60) > 0) {
                    738:         $total_time = int($total_time/60).' minutes, '.($total_time % 60);
                    739:     }
                    740:     $r->print('<br />'.$total_time.' seconds total');
                    741:     $r->rflush();
                    742:     return;
1.31      matthew   743: }
                    744: 
                    745: }
1.30      matthew   746: #######################################################
                    747: #######################################################
                    748: 
                    749: =pod
                    750: 
1.31      matthew   751: =head2 CSV output routines
                    752: 
                    753: =item &csv_initialize($r)
                    754: 
                    755: =item &csv_outputstudent($r,$student)
                    756: 
                    757: =item &csv_finish($r)
1.30      matthew   758: 
                    759: =cut
                    760: 
                    761: #######################################################
                    762: #######################################################
1.31      matthew   763: {
                    764: 
                    765: sub csv_initialize{
                    766:     my ($r) = @_;
                    767:     $r->print("<h1>Not implemented yet</h1>");
                    768:     return;
                    769: }
                    770: 
                    771: sub csv_outputstudent {
                    772:     my ($r,$student) = @_;
                    773: }
                    774: 
                    775: sub csv_finish {
                    776:     my ($r) = @_;
                    777: }
1.2       stredwic  778: 
                    779: }
                    780: 
1.28      matthew   781: #######################################################
                    782: #######################################################
                    783: 
1.2       stredwic  784: =pod
                    785: 
1.30      matthew   786: =item &StudentPerformanceOnSequence()
1.2       stredwic  787: 
1.30      matthew   788: Inputs:
1.2       stredwic  789: 
                    790: =over 4
                    791: 
1.30      matthew   792: =item $student
1.28      matthew   793: 
1.30      matthew   794: =item $studentdata Hash ref to all student data
1.2       stredwic  795: 
1.30      matthew   796: =item $seq Hash ref, the sequence we are working on
1.2       stredwic  797: 
1.30      matthew   798: =item $links if defined we will output links to each resource.
1.2       stredwic  799: 
1.28      matthew   800: =back
1.2       stredwic  801: 
                    802: =cut
1.1       stredwic  803: 
1.28      matthew   804: #######################################################
                    805: #######################################################
1.30      matthew   806: sub StudentPerformanceOnSequence {
1.32      matthew   807:     my ($student,$studentdata,$seq,$links) = @_;
1.31      matthew   808:     $links = 'no' if (! defined($links));
1.1       stredwic  809:     my $Str = '';
1.30      matthew   810:     my ($sum,$max) = (0,0);
                    811:     foreach my $resource (@{$seq->{'contents'}}) {
                    812:         next if ($resource->{'type'} ne 'assessment');
                    813:         my $resource_data = $studentdata->{$resource->{'symb'}};
                    814:         my $value = '';
                    815:         foreach my $partnum (@{$resource->{'parts'}}) {
                    816:             $max++;
                    817:             my $symbol = ' '; # default to space
                    818:             #
                    819:             if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {
                    820:                 my $status = $resource_data->{'resource.'.$partnum.'.solved'};
                    821:                 if ($status eq 'correct_by_override') {
                    822:                     $symbol = '+';
                    823:                     $sum++;
                    824:                 } elsif ($status eq 'incorrect_by_override') {
                    825:                     $symbol = '-';
                    826:                 } elsif ($status eq 'ungraded_attempted') {
                    827:                     $symbol = '#';
                    828:                 } elsif ($status eq 'incorrect_attempted')  {
                    829:                     $symbol = '.';
                    830:                 } elsif ($status eq 'excused') {
                    831:                     $symbol = 'x';
                    832:                     $max--;
                    833:                 } elsif ($status eq 'correct_by_student' &&
                    834:                     exists($resource_data->{'resource.'.$partnum.'.tries'})){
                    835:                     my $num = $resource_data->{'resource.'.$partnum.'.tries'};
                    836:                     if ($num > 9) {
                    837:                         $symbol = '*';
                    838:                     } elsif ($num > 0) {
                    839:                         $symbol = $num;
                    840:                     } else {
                    841:                         $symbol = ' ';
                    842:                     }
                    843:                     $sum++;
                    844:                 } else {
                    845:                     $symbol = ' ';
1.2       stredwic  846:                 }
1.30      matthew   847:             } else {
                    848:                 # Unsolved.  Did they try?
                    849:                 if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
                    850:                     $symbol = '.';
                    851:                 } else {
                    852:                     $symbol = ' ';
1.18      matthew   853:                 }
1.2       stredwic  854:             }
1.30      matthew   855:             #
1.31      matthew   856:             if ($links eq 'yes' && $symbol ne ' ') {
1.30      matthew   857:                 $symbol = '<a href="/adm/grades'.
                    858:                     '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
                    859:                         '&student='.$student->{'username'}.
                    860:                             '&domain='.$student->{'domain'}.
                    861:                                 '&command=submission">'.$symbol.'</a>';
                    862:             }
                    863:             $value .= $symbol;
1.2       stredwic  864:         }
1.30      matthew   865:         $Str .= $value;
1.17      minaeibi  866:     }
1.30      matthew   867:     return ($Str,$sum,$max);
1.17      minaeibi  868: }
                    869: 
1.28      matthew   870: #######################################################
                    871: #######################################################
1.17      minaeibi  872: sub StudentAverageTotal {
1.21      minaeibi  873:     my ($cache, $students, $sequenceKeys)=@_;
1.23      minaeibi  874:     my $Str = "\n<b>Summary Tables:</b>\n";
1.21      minaeibi  875:     my %Correct = ();
                    876:     my $ProblemsSolved = 0;
                    877:     my $TotalProblems = 0;
                    878:     my $StudentCount = 0;
                    879: 
                    880:     foreach my $name (@$students) {
                    881:         $StudentCount++;
                    882:         foreach my $sequence (@$sequenceKeys) {
1.23      minaeibi  883:             $Correct{$sequence} +=
                    884: 	       $cache->{$name.':'.$sequence.':problemsCorrect'};
1.17      minaeibi  885:         }
1.21      minaeibi  886: 	$ProblemsSolved += $cache->{$name.':problemsSolved'};
                    887:         $TotalProblems += $cache->{$name.':totalProblems'};
1.1       stredwic  888:     }
1.25      matthew   889:     if ($StudentCount) { 
1.27      minaeibi  890:         $ProblemsSolved = sprintf( "%.2f", 
                    891:                              $ProblemsSolved/$StudentCount);
1.25      matthew   892:         $TotalProblems /= $StudentCount;
                    893:     } else {
                    894:         $ProblemsSolved = 0;
                    895:         $TotalProblems  = 0;
                    896:     }
1.27      minaeibi  897: 
1.24      minaeibi  898:     $Str .= '<table border=2 cellspacing="1">'."\n";
1.23      minaeibi  899:     $Str .= '<tr><td><b>Students Count</b></td><td><b>'.
                    900:             $StudentCount.'</b></td></tr>'."\n";
                    901:     $Str .= '<tr><td><b>Total Problems</b></td><td><b>'.
                    902:             $TotalProblems.'</b></td></tr>'."\n";
                    903:     $Str .= '<tr><td><b>Average Correct</b></td><td><b>'.
                    904:             $ProblemsSolved.'</b></td></tr>'."\n";
                    905:     $Str .= '</table>'."\n";
                    906: 
1.24      minaeibi  907:     $Str .= '<table border=2 cellspacing="1">'."\n";
1.23      minaeibi  908:     $Str .= '<tr><th>Title</th><th>Total Problems</th>'.
                    909:             '<th>Average Correct</th></tr>'."\n";
                    910:     foreach my $S(@$sequenceKeys) {
                    911:         my $title=$cache->{$S.':title'};
                    912: 	#$Str .= $cache->{$S.':problems'};
1.24      minaeibi  913: 	#my @problems=split(':', $cache->{$S.':problems'});
1.23      minaeibi  914: 	#my $pCount=scalar @problems;
                    915: 	my $pCount=MaxSeqPr($cache,@$students[0],$S);
1.25      matthew   916:         my $crr;
                    917: 	if ($StudentCount) {
                    918:             $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
                    919:         } else {
                    920:             $crr="0.00";
                    921:         }
1.23      minaeibi  922:         $Str .= '<tr><td>'.$title.
                    923:                 '</td><td align=center>'.$pCount.
                    924:                 '</td><td align=center>'.$crr.
                    925:                 '</td></tr>'."\n";
1.10      stredwic  926:     }
1.1       stredwic  927: 
1.23      minaeibi  928:     $Str .= '</table>'."\n";
                    929: 
1.1       stredwic  930:     return $Str;
                    931: }
1.23      minaeibi  932: 
1.28      matthew   933: #######################################################
                    934: #######################################################
1.23      minaeibi  935: 
1.2       stredwic  936: =pod
                    937: 
                    938: =item &CreateLegend()
                    939: 
                    940: This function returns a formatted string containing the legend for the
                    941: chart.  The legend describes the symbols used to represent grades for
                    942: problems.
                    943: 
                    944: =cut
                    945: 
1.28      matthew   946: #######################################################
                    947: #######################################################
1.2       stredwic  948: sub CreateLegend {
                    949:     my $Str = "<p><pre>".
1.13      minaeibi  950:               "   1  correct by student in 1 try\n".
                    951:               "   7  correct by student in 7 tries\n".
1.12      minaeibi  952:               "   *  correct by student in more than 9 tries\n".
1.20      minaeibi  953: 	      "   +  correct by hand grading or override\n".
1.12      minaeibi  954:               "   -  incorrect by override\n".
                    955: 	      "   .  incorrect attempted\n".
                    956: 	      "   #  ungraded attempted\n".
1.13      minaeibi  957:               "      not attempted (blank field)\n".
1.12      minaeibi  958: 	      "   x  excused".
1.17      minaeibi  959:               "</pre><p>";
1.2       stredwic  960:     return $Str;
                    961: }
                    962: 
1.28      matthew   963: #######################################################
                    964: #######################################################
                    965: 
1.30      matthew   966: =pod 
1.2       stredwic  967: 
                    968: =back
                    969: 
                    970: =cut
                    971: 
1.28      matthew   972: #######################################################
                    973: #######################################################
1.2       stredwic  974: 
1.28      matthew   975: 1;
1.2       stredwic  976: 
1.1       stredwic  977: __END__

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