File:  [LON-CAPA] / loncom / interface / statistics / lonstudentsubmissions.pm
Revision 1.46: download - view: text, annotated - select for diffs
Fri Aug 18 15:15:38 2006 UTC (17 years, 10 months ago) by raeburn
Branches: MAIN
CVS tags: version_2_7_0, version_2_6_X, version_2_6_99_1, version_2_6_99_0, version_2_6_3, version_2_6_2, version_2_6_1, version_2_6_0, version_2_5_X, version_2_5_99_1, version_2_5_99_0, version_2_5_2, version_2_5_1, version_2_5_0, version_2_4_X, version_2_4_99_0, version_2_4_2, version_2_4_1, version_2_4_0, version_2_3_X, version_2_3_99_0, version_2_3_2, version_2_3_1, version_2_3_0, version_2_2_X, version_2_2_99_1, version_2_2_99_0, version_2_2_2, version_2_2_1, version_2_2_0, HEAD
Bug 4954.  Filter title changed from "Enrollment Status" to "Access Status".  Access Status selections in lonhtmlcommon set to:
Currently Has Access
Will Have Future Access
Previously Had Access
Any Access Status
See comment appended to bug 4954 for more information.
Documentation updated to include ability to selectively display students with future access.

    1: # The LearningOnline Network with CAPA
    2: #
    3: # $Id: lonstudentsubmissions.pm,v 1.46 2006/08/18 15:15:38 raeburn Exp $
    4: #
    5: # Copyright Michigan State University Board of Trustees
    6: #
    7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    8: #
    9: # LON-CAPA is free software; you can redistribute it and/or modify
   10: # it under the terms of the GNU General Public License as published by
   11: # the Free Software Foundation; either version 2 of the License, or
   12: # (at your option) any later version.
   13: #
   14: # LON-CAPA is distributed in the hope that it will be useful,
   15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17: # GNU General Public License for more details.
   18: #
   19: # You should have received a copy of the GNU General Public License
   20: # along with LON-CAPA; if not, write to the Free Software
   21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   22: #
   23: # /home/httpd/html/adm/gpl.txt
   24: #
   25: # http://www.lon-capa.org/
   26: #
   27: package Apache::lonstudentsubmissions;
   28: 
   29: use strict;
   30: use Apache::lonnet;
   31: use Apache::loncommon();
   32: use Apache::lonhtmlcommon();
   33: use Apache::loncoursedata();
   34: use Apache::lonstatistics;
   35: use Apache::lonlocal;
   36: use Apache::lonstathelpers;
   37: use HTML::Entities();
   38: use Time::Local();
   39: use Spreadsheet::WriteExcel();
   40: use lib '/home/httpd/lib/perl/';
   41: use LONCAPA;
   42:   
   43: 
   44: my @SubmitButtons = ({ name => 'SelectAnother',
   45:                        text => 'Choose a different Problem' },
   46:                      { name => 'Generate',
   47:                        text => 'Generate Report'},
   48:                      );
   49: 
   50: sub BuildStudentSubmissionsPage {
   51:     my ($r,$c)=@_;
   52:     #
   53:     my %Saveable_Parameters = ('Status' => 'scalar',
   54:                                'Section' => 'array',
   55:                                'NumPlots' => 'scalar',
   56:                                );
   57:     &Apache::loncommon::store_course_settings('student_submissions',
   58:                                               \%Saveable_Parameters);
   59:     &Apache::loncommon::restore_course_settings('student_submissions',
   60:                                                 \%Saveable_Parameters);
   61:     #
   62:     &Apache::lonstatistics::PrepareClasslist();
   63:     #
   64:     $r->print(&CreateInterface());
   65:     #
   66:     my @Students = @Apache::lonstatistics::Students;
   67:     #
   68:     if (@Students < 1) {
   69:         $r->print('<h2>There are no students in the sections selected</h2>');
   70:     }
   71:     #
   72:     my @CacheButtonHTML = 
   73:         &Apache::lonstathelpers::manage_caches($r,'Statistics','stats_status',
   74:                                    '<h3>'.&mt('Loading student data').'</h3>');
   75:     $r->rflush();
   76:     #
   77:     if (exists($env{'form.problemchoice'}) && 
   78:         ! exists($env{'form.SelectAnother'})) {
   79:         foreach my $button (@SubmitButtons) {
   80:             if ($button->{'name'} eq 'break') {
   81:                 $r->print("<br />\n");
   82:             } else {
   83:                 $r->print('<input type="submit" name="'.$button->{'name'}.'" '.
   84:                           'value="'.&mt($button->{'text'}).'" />');
   85:                 $r->print('&nbsp;'x5);
   86:             }
   87:         }
   88:         foreach my $html (@CacheButtonHTML) {
   89:             $r->print($html.('&nbsp;'x5));
   90:         }
   91:         #
   92:         $r->print('<hr />'.$/);
   93:         $r->rflush();
   94:         #
   95:         # Determine which problems we are to analyze
   96:         my @Symbs = 
   97:             &Apache::lonstathelpers::get_selected_symbs('problemchoice');
   98:         foreach my $selected (@Symbs) {
   99:             $r->print('<input type="hidden" name="problemchoice" value="'.
  100:                       $selected.'" />'.$/);
  101:         }
  102:         #
  103:         # Get resource objects
  104:         my $navmap = Apache::lonnavmaps::navmap->new();
  105:         if (!defined($navmap)) {
  106:             $r->print('<h1>'.&mt("Internal error").'</h1>');
  107:             return;
  108:         }
  109:         my %already_seen;
  110:         my @Problems;
  111:         foreach my $symb (@Symbs) {
  112:             my $resource = $navmap->getBySymb($symb);
  113:             push(@Problems,$resource);
  114:         }
  115:         # 
  116:         $r->print('<h4>'.
  117:                   &Apache::lonstatistics::section_and_enrollment_description().
  118:                   '</h4>');
  119:         if (! scalar(@Problems) || ! defined($Problems[0])) {
  120:             $r->print('resource is undefined');
  121:         } else {
  122:             if (scalar(@Problems) == 1) {
  123:                 my $resource = $Problems[0];
  124:                 $r->print('<h1>'.$resource->title.'</h1>');
  125:                 $r->print('<h3>'.$resource->src.'</h3>');
  126:                 if ($env{'form.renderprob'} eq 'true') {
  127:                     $r->print(&Apache::lonstathelpers::render_resource($resource));
  128:                     $r->rflush();
  129:                 }
  130:             }
  131:             if ($env{'form.output'} eq 'excel') {
  132:                 &prepare_excel_output($r,\@Problems,\@Students);
  133:             } elsif ($env{'form.output'} eq 'csv') {
  134:                 &prepare_csv_output($r,\@Problems,\@Students);
  135:             } else {
  136:                 &prepare_html_output($r,\@Problems,\@Students);
  137:             }
  138:         }
  139:         $r->print('<hr />');
  140:     } else {
  141:         $r->print('<input type="submit" name="Generate" value="'.
  142:                   &mt('Prepare Report').'" />');
  143:         $r->print('&nbsp;'x5);
  144:         $r->print('<p>'.
  145:                   &mt('Computing correct answers greatly increasese the amount of time required to prepare a report.').
  146:                   '</p>');
  147:         $r->print('<p>'.
  148:                   &mt('please select problems and use the <b>Prepare Report</b> button to continue.').
  149:                   '</p>');
  150:         $r->print(&Apache::lonstathelpers::MultipleProblemSelector
  151:                   (undef,'problemchoice','Statistics'));
  152:     }
  153: }
  154: 
  155: ##
  156: ## get_extra_response_headers
  157: ##
  158: sub get_extra_response_headers {
  159:     my @extra_resp_headers;
  160:     if ($env{'form.correctans'} eq 'true') {
  161:         push(@extra_resp_headers,'Correct');
  162:     } 
  163:     if ($env{'form.prob_status'} eq 'true') {
  164:         push(@extra_resp_headers,'Award Detail'); 
  165:         push(@extra_resp_headers,'Time');
  166:         push(@extra_resp_headers,'Attempt');
  167:         push(@extra_resp_headers,'Awarded');
  168:     }
  169:     return @extra_resp_headers;
  170: }
  171: 
  172: ##
  173: ## get_headers:
  174: ##     return the proper headers for the given response 
  175: sub get_headers {
  176:     my ($prob,$partid,$respid,$resptype,$analysis,$output,$purpose,
  177:         @basic_headers) = @_;
  178:     my @headers;
  179:     if ($resptype eq 'essay' && $purpose eq 'display' &&
  180:         ($output eq 'html')) {# || scalar(@{$prob->parts})!=1)) {
  181:         @headers = ();
  182:     } elsif ($resptype =~ /^(option|match|rank)$/) {
  183:         my $prefix = '_';
  184:         if ($purpose eq 'display') {
  185:             $prefix = '';
  186:         }
  187:         my @foils = 
  188:             map { 
  189:                 $prefix.$_; 
  190:             } sort(keys(%{$analysis->{$partid.'.'.$respid}->{'_Foils'}}));
  191:         if (scalar(@basic_headers) && $basic_headers[0] eq 'Correct') {
  192:             @foils = map { ($_ , $_.' Correct') } @foils;
  193:             shift(@basic_headers);  # Get rid of 'Correct'
  194:         }
  195:         @headers = (@foils,@basic_headers);
  196:     } elsif (lc($resptype) eq 'task') {
  197:         @headers = ('Grader','Status',@basic_headers,'Submission');
  198:     } else {
  199:         @headers = ('Submission',@basic_headers);
  200:     }
  201:     return @headers;
  202: }
  203: 
  204: #########################################################
  205: #########################################################
  206: ##
  207: ##    HTML Output Routines
  208: ##
  209: #########################################################
  210: #########################################################
  211: sub prepare_html_output {
  212:     my ($r,$problems,$students) = @_;
  213:     my $c = $r->connection();
  214:     #
  215:     # Set a flag for the case when there is just one problem
  216:     my $single_response = 0;
  217:     if (scalar(@$problems) == 1 &&
  218:         $problems->[0]->countResponses == 1) {
  219:         $single_response = 1;
  220:     }
  221:     #
  222:     # Compute the number of columns per response
  223:     my @extra_resp_headers = &get_extra_response_headers();
  224:     #
  225:     # Create the table header
  226:     my @student_columns = ('username','domain','id','section');
  227:     #
  228:     my %headers;
  229:     my $student_column_count = scalar(@student_columns);
  230:     $headers{'problem'} = qq{<th colspan="$student_column_count">\&nbsp;</th>};
  231:     foreach (@student_columns) {
  232:         $headers{'student'}.= '<th>'.ucfirst($_).'</th>';
  233:     }
  234:     #
  235:     # we put the headers into the %headers hash
  236:     my $total_col = scalar(@student_columns);
  237:     my $nonempty_part_headers = 0;
  238:     #
  239:     my %problem_analysis;
  240:     foreach my $prob (@$problems) {
  241:         my %analysis = &Apache::lonstathelpers::get_problem_data($prob->src);
  242:         $problem_analysis{$prob->src}=\%analysis;
  243:         #
  244:         my $prob_span = 0;
  245:         my $single_part = 0;
  246:         if (scalar(@{$prob->parts}) == 1) {
  247:             $single_part = 1;
  248:         }
  249:         foreach my $partid (@{$prob->parts}) {
  250:             my $part_span = 0;
  251:             my $responses = [$prob->responseIds($partid)];
  252:             my $resptypes = [$prob->responseType($partid)];
  253:             for (my $i=0;$i<scalar(@$responses);$i++) {
  254:                 my $respid = $responses->[$i];
  255:                 my @headers = &get_headers($prob,$partid,$respid,
  256:                                            $resptypes->[$i],
  257:                                            $problem_analysis{$prob->src},
  258:                                            'html','display',
  259:                                            @extra_resp_headers);
  260:                 if (scalar(@headers)>0) {
  261:                     $total_col += scalar(@headers);
  262:                     $part_span += scalar(@headers);
  263:                     $headers{'response'} .=
  264:                         '<th colspan="'.scalar(@headers).'">'.
  265:                         &mt('Response [_1]',$responses->[$i]).'</th>';
  266:                     $headers{'student'}.= '<th>'.join('</th><th><nobr>',
  267:                                                       @headers).
  268:                                                           '</nobr></th>';
  269:                 }
  270:             }
  271:             if ($part_span == 0) {
  272:                 next;
  273:             }
  274:             if (! $single_part) {
  275:                 my $tmpname = $partid;
  276:                 if ($partid =~/^\d+$/) {
  277:                     $tmpname = $prob->part_display($partid);
  278:                 }
  279:                 if ($tmpname !~ /^part/) {
  280:                     $tmpname = 'Part '.$tmpname;
  281:                 }
  282:                 $headers{'part'} .= qq{<th colspan="$part_span">$tmpname</th>};
  283:                 $nonempty_part_headers = 1;
  284:             } else {
  285:                 $headers{'part'} .= qq{<th colspan="$part_span">&nbsp</th>};
  286:             }
  287:             $prob_span += $part_span;
  288:         }
  289:         my $title = $prob->compTitle;
  290:         if ($prob_span > 0) {
  291:             $headers{'problem'}.= qq{<th colspan="$prob_span">$title</th>};
  292:         } elsif ($single_response) {
  293:             $prob_span = scalar(@student_columns);
  294:             $headers{'problem'} = qq{<th colspan="$prob_span">$title</th>};
  295:         }
  296:     }
  297:     if (exists($headers{'part'})) {
  298:         $headers{'part'} = qq{<th colspan="$student_column_count">\&nbsp;</th>}.
  299:             $headers{'part'};
  300:     }
  301:     if (exists($headers{'response'})) {
  302:         $headers{'response'}=
  303:             qq{<th colspan="$student_column_count">\&nbsp;</th>}.
  304:             $headers{'response'};
  305:     }
  306:     my $full_header = $/.'<table>'.$/;
  307:     $full_header .= '<tr align="left">'.$headers{'problem'}.'</tr>'.$/;
  308:     if ($nonempty_part_headers) {
  309:         $full_header .= '<tr align="left">'.$headers{'part'}.'</tr>'.$/;
  310:     }
  311:     $full_header .= '<tr align="left">'.$headers{'response'}.'</tr>'.$/;
  312:     $full_header .= '<tr align="left">'.$headers{'student'}.'</tr>'.$/;
  313:     #
  314:     # Main loop
  315:     my $count;
  316:     $r->print($/.$full_header.$/);
  317:     my $row_class = 'odd';   # css 
  318:     foreach my $student (@$students) {
  319:         my $student_row_data;
  320:         if ($count++ >= 30) {
  321:             $r->print('</table>'.$/.$full_header.$/);
  322:             $count = 0;
  323:         }
  324:         last if ($c->aborted());
  325:         foreach my $field (@student_columns) {
  326:             $student_row_data .= 
  327:                 '<td valign="top">'.$student->{$field}.'</td>';
  328:         }
  329:         #
  330:         # Figure out what it is we need to output for this student
  331:         my @essays;
  332:         my %prob_data;
  333:         my $maxrow;
  334:         foreach my $prob (@$problems) {
  335:             $prob_data{$prob->symb}={};
  336:             foreach my $partid (@{$prob->parts}) {
  337:                 my @responses = $prob->responseIds($partid);
  338:                 my @response_type = $prob->responseType($partid);
  339:                 for (my $i=0;$i<=$#responses;$i++) {
  340:                     my $respid  = $responses[$i];
  341:                     my $results = 
  342:                         &Apache::loncoursedata::get_response_data_by_student
  343:                         ($student,$prob->symb(),$respid);
  344:                     my $resptype = $response_type[$i];
  345:                     my @headers = &get_headers($prob,$partid,$respid,
  346:                                                $resptype,
  347:                                                $problem_analysis{$prob->src},
  348:                                                'html','normal',
  349:                                                @extra_resp_headers);
  350:                     my $width = scalar(@headers);
  351:                     next if ($width < 1);
  352:                     my $resp_data;
  353:                     $resp_data->{'fake'} = qq{<td colspan="$width">&nbsp;</td>};
  354:                     if (! defined($results)) {
  355:                         $results = [];
  356:                     }
  357:                     # 
  358:                     if (scalar(@$results) > $maxrow && $resptype ne 'essay') {
  359:                         $maxrow = scalar(@$results);
  360:                     }
  361:                     for (my $j=scalar(@$results)-1;$j>=0;$j--) {
  362:                         if ($env{'form.all_sub'} ne 'true') {
  363:                             next if ($j ne scalar(@$results)-1);
  364:                         }
  365:                         my $response = &hashify_response($results->[$j],
  366:                                                          $prob,
  367:                                                          $student,
  368:                                                          $partid,
  369:                                                          $respid);
  370:                         if ($resptype eq 'essay') {
  371:                             push(@essays,
  372:                                  &html_essay_results(\@headers,
  373:                                                      $prob,$partid,$respid,
  374:                                                      $response,
  375:                                                      $single_response).
  376:                                  '</td>');
  377: 			} elsif (lc($resptype) eq 'task') {
  378: 			    my $results = 
  379: 				&html_task_results(\@headers,
  380: 						   $prob,$partid,$respid,
  381: 						   $response,$resptype);
  382: 			    if ($results) {
  383: 				push(@{$resp_data->{'real'}},$results);
  384: 			    }
  385:                         } else {
  386:                             push(@{$resp_data->{'real'}},
  387:                                  &html_non_essay_results(\@headers,
  388:                                                          $prob,$partid,$respid,
  389:                                                          $response,$resptype));
  390:                         }
  391:                     }
  392:                     $prob_data{$prob->symb}->{$partid}->{$respid}=$resp_data;
  393:                 } # end of $i loop
  394:             } # end of partid loop
  395:         } # end of prob loop
  396:         #
  397:         # if there is no data, skip this student.
  398:         next if (! $maxrow && ! scalar(@essays));
  399:         #
  400:         # Go through the problem data and output a row.
  401:         if ($row_class eq 'even') {
  402:             $row_class = 'odd'; 
  403:         } else {
  404:             $row_class = 'even'; 
  405:         }
  406:         my $printed_something;
  407:         for (my $rows_output = 0;$rows_output<$maxrow;$rows_output++) {
  408:             my $html;
  409:             my $no_data = 1;
  410:             foreach my $prob (@$problems) {
  411:                 foreach my $partid (@{$prob->parts}) {
  412:                     my @responses     = $prob->responseIds($partid);
  413:                     my @response_type = $prob->responseType($partid);
  414:                     for (my $i=0;$i<=$#responses;$i++) {
  415:                         my $respid   = $responses[$i];
  416:                         my $resp_data = 
  417:                             $prob_data{$prob->symb}->{$partid}->{$respid};
  418:                         next if ($response_type[$i] eq 'essay');
  419:                         if (defined($resp_data->{'real'}->[$rows_output])) {
  420:                             $html .= $resp_data->{'real'}->[$rows_output];
  421:                             $no_data = 0;
  422:                         } else {
  423:                             $html .= $resp_data->{'fake'};
  424:                         }
  425:                     }
  426:                 }
  427:             }
  428:             if (! $no_data) {
  429:                 $r->print(qq{<tr class="$row_class">$student_row_data$html</tr>}.$/);
  430:                 $printed_something=1;
  431:             }
  432:         }
  433:         if (@essays) {
  434:             my $tr = qq{<tr class="$row_class">};
  435:             my $td = qq{<td  valign="top" class="essay" colspan="$total_col">};
  436:             if (! $printed_something) {
  437:                 $r->print($tr.$student_row_data.'</tr>'.$/);
  438:             }
  439:             $r->print($tr.$td.
  440:                       join('</td></tr>'.$/.$tr.$td,@essays).'</td></tr>'.$/);
  441:             undef(@essays);
  442:         }
  443:     } # end of student loop
  444:     return;
  445: }
  446: 
  447: sub hashify_response {
  448:     my ($response,$prob,$student,$partid,$respid) =@_;
  449:     my $resp_hash = {};
  450:     if ($env{'form.correctans'} eq 'true') {
  451:         $resp_hash->{'Correct'} = 
  452:             &Apache::lonstathelpers::get_student_answer
  453:             ($prob,$student->{'username'},$student->{'domain'},
  454:              $partid,$respid);
  455:     }
  456:     $resp_hash->{'Submission'} = 
  457:         $response->[&Apache::loncoursedata::RDs_submission()];
  458:     $resp_hash->{'Time'} = 
  459:         $response->[&Apache::loncoursedata::RDs_timestamp()];
  460:     $resp_hash->{'Attempt'} =
  461:         $response->[&Apache::loncoursedata::RDs_tries()];
  462:     $resp_hash->{'Awarded'} = 
  463:         $response->[&Apache::loncoursedata::RDs_awarded()];
  464:     if ($prob->is_task()) {
  465: 	$resp_hash->{'Grader'} = 
  466: 	    $response->[&Apache::loncoursedata::RDs_response_eval_2()];
  467: 	if ($resp_hash->{'Attempt'} eq '0') {
  468: 	    $resp_hash->{'Attempt'} = '';
  469: 	}
  470: 	$resp_hash->{'Award Detail'} = 
  471: 	    $response->[&Apache::loncoursedata::RDs_part_award()];
  472: 	$resp_hash->{'Status'} = 
  473: 	    $response->[&Apache::loncoursedata::RDs_response_eval()];
  474:     } else {
  475: 	$resp_hash->{'Award Detail'} = 
  476: 	    $response->[&Apache::loncoursedata::RDs_awarddetail()];
  477:     }
  478: 
  479:     return $resp_hash;
  480: }
  481: 
  482: #####################################################
  483: ##
  484: ##     HTML helper routines
  485: ##
  486: #####################################################
  487: sub html_essay_results {
  488:     my ($headers,$prob,$partid,$respid,$response,$single_response)=@_;
  489:     if (! ref($headers) || ref($headers) ne 'ARRAY') {
  490:         return '';
  491:     }
  492:     # Start of telling them what problem, part, and response
  493:     my $Str;
  494:     if (! $single_response) {
  495:         my $id = $prob->compTitle;
  496:         if (defined($partid) && $partid ne '0') {
  497:             $id .= ' '.$prob->part_display($partid);
  498:         }
  499:         if (defined($respid)) {
  500:             $id .= ' '.$respid;
  501:         }
  502:         $Str .= '<nobr>'.$id.'</nobr>'.('&nbsp;'x4);
  503:     }
  504:     #
  505:     shift(@$headers); # Get rid of the Submission header
  506:     my $correct = '';
  507:     if ($headers->[0] eq 'Correct') {
  508:         $correct = &html_format_essay_sub($response->{'Correct'});
  509:         shift(@$headers);
  510:     }
  511:     $Str .= '<nobr>'.
  512:         join('',
  513:              map {
  514:                  ('&nbsp;'x4).&mt($_.': [_1]',$response->{$_});
  515:              } @$headers).'</nobr>';
  516:     if (@$headers || ! $single_response) {
  517:         $Str .= '<br />';
  518:     }
  519:     $Str .= &html_format_essay_sub($response->{'Submission'});
  520:     #
  521:     if (defined($correct) && $correct !~ /^\s*$/) {
  522:         $Str .= '<hr /><b>'.&mt('Correct').'</b>'.$correct
  523:     }
  524:     return $Str;
  525: }
  526: 
  527: sub html_format_essay_sub {
  528:     my ($submission) = @_;
  529:     return '' if (! defined($submission) || $submission eq '');
  530:     $submission = &HTML::Entities::decode($submission);
  531:     $submission =~ s/\\\"/\"/g;
  532:     $submission =~ s/\\\'/\'/g;
  533:     $submission =~ s|\\r\\n|$/|g;
  534:     $submission = &HTML::Entities::encode($submission,'<>&"');
  535:     $submission =~ s|$/\s*$/|$/</p><p>$/|g;
  536:     $submission =~ s|\\||g;
  537:     $submission = '<p>'.$submission.'</p>';
  538:     return $submission;
  539: }
  540: 
  541: sub html_task_results {
  542:     my ($headers,$prob,$partid,$respid,$response,$resptype) = @_;
  543:     if (! ref($headers) || ref($headers) ne 'ARRAY' || ! scalar(@$headers)) {
  544:         return '';
  545:     }
  546: 
  547:     my @values;
  548:     @values = map { $response->{$_}; } @$headers;
  549: 
  550:     my $td = '<td valign="top">';
  551:     my $str = $td.join('</td>'.$td,@values).'</td>';
  552:     return $str;
  553: }
  554: 
  555: sub html_non_essay_results {
  556:     my ($headers,$prob,$partid,$respid,$response,$resptype) = @_;
  557:     if (! ref($headers) || ref($headers) ne 'ARRAY' || ! scalar(@$headers)) {
  558:         return '';
  559:     }
  560:     # 
  561:     my $submission = &HTML::Entities::decode(&unescape($response->{'Submission'})); 
  562:     return '' if (! defined($submission) || $submission eq '');
  563:     $submission =~ s/\\\"/\"/g;
  564:     $submission =~ s/\\\'/\'/g;
  565:     if ($resptype eq 'radiobutton') {
  566:         $submission = &HTML::Entities::encode($submission,'<>&"');
  567:         $submission =~ s/=([^=])$//;
  568:         $submission = '<nobr>'.$submission.'</nobr>';
  569:     }
  570:     $response->{'Submission'} = $submission;
  571:     #
  572:     my @values;
  573:     if ($resptype =~ /^(option|match|rank)$/) {
  574:         my %submission = 
  575:             map { 
  576:                 my ($foil,$value) = split('=',&unescape($_));
  577:                 ($foil,$value);
  578:             } split('&',$response->{'Submission'});
  579:         my %correct;
  580:         if (exists($response->{'Correct'})) {
  581:             %correct = 
  582:                 map { 
  583:                     my ($foil,$value)=split('=',&unescape($_));
  584:                     ($foil,$value);
  585:                 } split('&',$response->{'Correct'});
  586:         }
  587:         #
  588:         foreach my $original_header (@$headers) {
  589:             if ($original_header =~ /^_/) {
  590:                 # '_' denotes a foil column
  591:                 my ($header) = ($original_header =~ m/^_(.*)$/);
  592:                 my $option = '';
  593:                 if ( my ($foil) = ($header =~ /(.*) Correct$/)) {
  594:                     if (exists($correct{$foil})) {
  595:                         $option = $correct{$foil};
  596:                     }
  597:                 } elsif (exists($submission{$header})) {
  598:                     $option = $submission{$header};
  599:                 }
  600:                 push(@values,&HTML::Entities::encode($option));
  601:             } elsif ($original_header eq 'Time') {
  602:                 push(@values,&Apache::lonlocal::locallocaltime($response->{$original_header}));
  603:             } else {
  604:                 # A normal column
  605:                 push(@values,$response->{$original_header});
  606:             }
  607:         }
  608:     } else {
  609:         @values = map { $response->{$_}; } @$headers;
  610:     }
  611:     my $td = '<td valign="top">';
  612:     my $str = $td.join('</td>'.$td,@values).'</td>';
  613:     return $str;
  614: }
  615: 
  616: 
  617: #########################################################
  618: #########################################################
  619: ##
  620: ##    Excel Output Routines
  621: ##
  622: #########################################################
  623: #########################################################
  624: sub prepare_excel_output {
  625:     my ($r,$Problems,$Students) = @_;
  626:     my $c = $r->connection();
  627:     #
  628:     #
  629:     # Determine the number of columns in the spreadsheet
  630:     my $columncount = 3; # username, domain, id
  631:     my @extra_resp_headers = &get_extra_response_headers();
  632:     my $lastprob;
  633:     my %problem_analysis;
  634:     foreach my $prob (@$Problems) {
  635:         my %analysis = &Apache::lonstathelpers::get_problem_data($prob->src);
  636:         $problem_analysis{$prob->src}=\%analysis;
  637:         foreach my $partid (@{$prob->parts}) {
  638:             my $responses = [$prob->responseIds($partid)];
  639:             my $resptypes = [$prob->responseType($partid)];
  640:             for (my $i=0;$i<scalar(@$responses);$i++) {
  641:                 my @headers = &get_headers($prob,$partid,$responses->[$i],
  642:                                            $resptypes->[$i],
  643:                                            $problem_analysis{$prob->src},
  644:                                            'excel','display',
  645:                                            @extra_resp_headers);
  646:                 $columncount += scalar(@headers);
  647:             }
  648:         }
  649:         last if ($columncount > 255);
  650:         $lastprob = $prob;
  651:     }
  652:     if ($columncount > 255) {
  653:         $r->print('<h1>'.&mt('Unable to complete request').'</h1>'.$/.
  654:                   '<p>'.&mt('LON-CAPA is unable to produce your Excel spreadsheet because your selections will result in more than 255 columns.  Excel allows only 255 columns in a spreadsheet.').'</p>'.$/.
  655:                   '<p>'.&mt('Consider selecting fewer problems to generate reports on, or reducing the number of items per problem.  Or use HTML or CSV output.').'</p>'.$/.
  656:                   '<p>'.&mt('The last problem that will fit in the current spreadsheet is [_1].',$lastprob->compTitle).'</p>');
  657:         $r->rflush();
  658:         return;
  659:     }
  660:     #
  661:     # Print out a message telling them what we are doing
  662:     if (scalar(@$Problems) > 1) {
  663:         $r->print('<h2>'.
  664:                   &mt('Preparing Excel spreadsheet of student responses to [_1] problems',
  665:                       scalar(@$Problems)).
  666:                   '</h2>');
  667:     } else {
  668:         $r->print('<h2>'.
  669:                   &mt('Preparing Excel spreadsheet of student responses').
  670:                   '</h2>');
  671:     }
  672:     $r->rflush();
  673:     #
  674:     # Create the excel spreadsheet
  675:     my ($workbook,$filename,$format) = 
  676:         &Apache::loncommon::create_workbook($r);
  677:     return if (! defined($workbook));
  678:     my $worksheet  = $workbook->addworksheet('Student Submission Data');
  679:     #
  680:     # Add headers to the worksheet
  681:     my $rows_output = 0;
  682:     $worksheet->write($rows_output++,0,
  683:                     $env{'course.'.$env{'request.course.id'}.'.description'},
  684:                       $format->{'h1'});
  685:     $rows_output++;
  686:     my $cols_output = 0;
  687:     my $title_row  = $rows_output++;
  688:     my $partid_row = $rows_output++;
  689:     my $respid_row = $rows_output++;
  690:     my $header_row = $rows_output++;
  691:     $worksheet->write($title_row ,0,'Problem Title',$format->{'bold'});
  692:     $worksheet->write($partid_row,0,'Part ID',$format->{'bold'});
  693:     $worksheet->write($respid_row,0,'Response ID',$format->{'bold'});
  694:     # Student headers
  695:     my @StudentColumns = ('username','domain','id','section');
  696:     foreach (@StudentColumns) {
  697:         $worksheet->write($header_row,$cols_output++,ucfirst($_),
  698:                           $format->{'bold'});
  699:     }
  700:     # Problem headers
  701:     my %start_col;
  702:     foreach my $prob (@$Problems) {
  703:         my $title = $prob->compTitle;
  704:         $worksheet->write($title_row,$cols_output,
  705:                           $title,$format->{'h3'});
  706:         foreach my $partid (@{$prob->parts}) {
  707:             $worksheet->write($partid_row,$cols_output,
  708:                               $prob->part_display($partid));
  709:             my $responses = [$prob->responseIds($partid)];
  710:             my $resptypes = [$prob->responseType($partid)];
  711:             for (my $i=0;$i<scalar(@$responses);$i++) {
  712:                 $start_col{$prob->symb}->{$partid}->{$responses->[$i]}=
  713:                     $cols_output;
  714:                 $worksheet->write($respid_row,$cols_output,
  715:                                   $resptypes->[$i].', '.$responses->[$i]);
  716:                 my @headers = &get_headers($prob,$partid,$responses->[$i],
  717:                                            $resptypes->[$i],
  718:                                            $problem_analysis{$prob->src},
  719:                                            'excel','display',
  720:                                            @extra_resp_headers);
  721:                 foreach my $text (@headers) {
  722:                     if ($text eq 'Time') {
  723:                         $worksheet->set_column($cols_output,$cols_output,undef,
  724:                                                $format->{'date'});
  725:                     } 
  726:                     $worksheet->write($header_row,$cols_output++,$text);
  727:                 }
  728:             }
  729:         }
  730:     }
  731:     #
  732:     # Populate the worksheet with the student data
  733:     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
  734:         ($r,'Excel File Compilation Status',
  735:          'Excel File Compilation Progress', 
  736:          scalar(@$Students),'inline',undef,'Statistics','stats_status');
  737:     my $max_row = $rows_output;
  738:     foreach my $student (@$Students) {
  739:         last if ($c->aborted());
  740:         $cols_output = 0;
  741:         my $student_row = $max_row;
  742:         foreach my $field (@StudentColumns) {
  743:             $worksheet->write($student_row,$cols_output++,
  744:                               $student->{$field});
  745:         }
  746:         my $last_student_col = $cols_output-1;
  747:         foreach my $prob (@$Problems) {
  748:             foreach my $partid (@{$prob->parts}) {
  749:                 my @Response = $prob->responseIds($partid);
  750:                 my @ResponseType = $prob->responseType($partid);
  751:                 for (my $i=0;$i<=$#Response;$i++) {
  752:                     my $respid   = $Response[$i];
  753:                     my $resptype = $ResponseType[$i];
  754:                     my $results = 
  755:                         &Apache::loncoursedata::get_response_data_by_student
  756:                         ($student,$prob->symb(),$respid);
  757:                     my @headers = &get_headers($prob,$partid,$respid,
  758:                                                $resptype,
  759:                                                $problem_analysis{$prob->src},
  760:                                                'excel','normal',
  761:                                                @extra_resp_headers);
  762: 
  763:                     if (! defined($results)) {
  764:                         $results = [];
  765:                     }
  766:                     #
  767:                     $rows_output = $student_row;
  768:                     #
  769:                     my $response_start_col = $start_col{$prob->symb}->{$partid}->{$respid};
  770:                     for (my $j=scalar(@$results)-1;$j>=0;$j--) {
  771:                         $cols_output = $response_start_col;
  772:                         if ($env{'form.all_sub'} ne 'true') {
  773:                             next if ($j ne scalar(@$results)-1);
  774:                         }
  775:                         my $response = &hashify_response($results->[$j],
  776:                                                          $prob,
  777:                                                          $student,
  778:                                                          $partid,
  779:                                                          $respid);
  780:                         my @response_data = 
  781:                             &compile_response_data(\@headers,$response,
  782:                                                    $prob,$partid,$respid,
  783:                                                    $resptype,
  784:                                                    \&excel_format_item);
  785:                         $worksheet->write_row($rows_output++,$cols_output,
  786:                                               \@response_data);
  787:                         $cols_output+=scalar(@response_data);
  788:                         if ($rows_output > $max_row) {
  789:                             $max_row = $rows_output;
  790:                         }
  791:                     }
  792:                 }
  793:             }
  794:         }
  795:         # Fill in the remaining rows with the students data
  796:         for (my $row = $student_row+1;$row<$max_row;$row++) {
  797:             my $cols = 0;
  798:             foreach my $field (@StudentColumns) {
  799:                 $worksheet->write($row,$cols++,
  800:                                   $student->{$field});
  801:             }
  802:         }
  803:         &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
  804:                                                  'last student');
  805:     }
  806:     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
  807:     #
  808:     # Close the excel file
  809:     $workbook->close();
  810:     #
  811:     # Write a link to allow them to download it
  812:     $r->print('<p><a href="'.$filename.'">'.
  813:               &mt('Your Excel spreadsheet.').
  814:               '</a></p>'."\n");
  815:     $r->print('<script>'.
  816:               'window.document.Statistics.stats_status.value="'.
  817:               'Done compiling spreadsheet.  See link below to download.'.
  818:               '";</script>');
  819:     $r->rflush();
  820:     return;
  821: }
  822: 
  823: sub compile_response_data {
  824:     my ($headers,$response,$prob,$partid,$respid,$resptype,$format) = @_;
  825:     if (! ref($headers) || ref($headers) ne 'ARRAY' || ! scalar(@$headers)) {
  826:         return ();
  827:     }
  828:     if (ref($format) ne 'CODE') {
  829:         $format = sub { return $_[0]; };
  830:     }
  831:     #
  832:     my $submission = 
  833:         &HTML::Entities::decode
  834:         (&unescape($response->{'Submission'}));
  835:     if (!$prob->is_task()) {
  836: 	return () if (! defined($submission) || $submission eq '');
  837:     }
  838:     $submission =~ s/\\\"/\"/g;
  839:     $submission =~ s/\\\'/\'/g;
  840:     if ($resptype eq 'radiobutton') {
  841:         $submission =~ s/=([^=])$//;
  842:     }
  843:     $response->{'Submission'} = $submission;
  844:     #
  845:     my @values;
  846:     if ($resptype =~ /^(option|match|rank)$/) {
  847:         my %submission = 
  848:             map { 
  849:                 my ($foil,$value) = split('=',&unescape($_));
  850:                 ($foil,$value);
  851:             } split('&',$response->{'Submission'});
  852:         my %correct;
  853:         if (exists($response->{'Correct'})) {
  854:             %correct = 
  855:                 map { 
  856:                     my ($foil,$value)=split('=',&unescape($_));
  857:                     ($foil,$value);
  858:                 } split('&',$response->{'Correct'});
  859:         }
  860:         #
  861:         foreach my $original_header (@$headers) {
  862:             if ($original_header =~ /^_/) {
  863:                 # '_' denotes a foil column
  864:                 my ($header) = ($original_header =~ m/^_(.*)$/);
  865:                 my $option = '';
  866:                 if ( my ($foil) = ($header =~ /(.*) Correct$/)) {
  867:                     if (exists($correct{$foil})) {
  868:                         $option = $correct{$foil};
  869:                     }
  870:                 } elsif (exists($submission{$header})) {
  871:                     $option = $submission{$header};
  872:                 }
  873:                 push(@values,&{$format}($option,$header));
  874:             } else {
  875:                 # A normal column
  876:                 push(@values,&{$format}($response->{$original_header},
  877:                                         $original_header));
  878:             }
  879:         }
  880:     } else {
  881:         @values = map { &{$format}($response->{$_},$_); } @$headers;
  882:     }
  883:     return @values;
  884: }
  885: 
  886: sub excel_format_item {
  887:     my ($item,$type) = @_;
  888:     if ($type eq 'Time') {
  889:         $item = &Apache::lonstathelpers::calc_serial($item);
  890:     } else {
  891:         if ($item =~ m/^=/) {
  892:             $item = ' '.$item;
  893:         }
  894:         $item =~ s/\\r//g;
  895:         $item =~ s/\\n/\n/g;
  896:         $item =~ s/(\s*$|^\s*)//g;
  897:         $item =~ s/\\\'/\'/g;
  898:     }
  899:     return $item;
  900: }
  901: 
  902: #########################################################
  903: #########################################################
  904: ##
  905: ##      CSV output of student answers
  906: ##
  907: #########################################################
  908: #########################################################
  909: sub prepare_csv_output {
  910:     my ($r,$problems,$students) = @_;
  911:     my $c = $r->connection();
  912:     #
  913:     $r->print('<h2>'.
  914:               &mt('Generating CSV report of student responses').'</h2>');
  915:     #
  916:     # Progress window
  917:     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
  918:         ($r,'CSV File Compilation Status',
  919:          'CSV File Compilation Progress', 
  920:          scalar(@$students),'inline',undef,'Statistics','stats_status');
  921:     
  922:     $r->rflush();
  923:     #
  924:     # Open a file
  925:     my $outputfile;
  926:     my $filename = '/prtspool/'.
  927:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
  928:             time.'_'.rand(1000000000).'.csv';
  929:     unless ($outputfile = Apache::File->new('>/home/httpd'.$filename)) {
  930:         $r->log_error("Couldn't open $filename for output $!");
  931:         $r->print("Problems occured in writing the csv file.  ".
  932:                   "This error has been logged.  ".
  933:                   "Please alert your LON-CAPA administrator.");
  934:         $outputfile = undef;
  935:     }
  936:     #
  937:     # Compute the number of columns per response
  938:     my @extra_resp_headers = &get_extra_response_headers();
  939:     #
  940:     # Create the table header
  941:     my @student_columns = ('username','domain','id','section');
  942:     #
  943:     my %headers;
  944:     push(@{$headers{'student'}},@student_columns);
  945:     # Pad for the student data
  946:     foreach my $row ('problem','part','response') {
  947:         $headers{$row}=[map {''} @student_columns];
  948:     }
  949:     #
  950:     # we put the headers into the %headers hash
  951:     my %problem_analysis;
  952:     my %start_col;
  953:     my $max_column = scalar(@student_columns);
  954:     foreach my $prob (@$problems) {
  955:         my %analysis = &Apache::lonstathelpers::get_problem_data($prob->src);
  956:         $problem_analysis{$prob->src}=\%analysis;
  957:         $headers{'problem'}->[$max_column] = $prob->compTitle;
  958:         foreach my $partid (@{$prob->parts}) {
  959:             $headers{'part'}->[$max_column] = $prob->part_display($partid);
  960:             my $responses = [$prob->responseIds($partid)];
  961:             my $resptypes = [$prob->responseType($partid)];
  962:             for (my $i=0;$i<scalar(@$responses);$i++) {
  963:                 my @headers = &get_headers($prob,$partid,$responses->[$i],
  964:                                            $resptypes->[$i],
  965:                                            $problem_analysis{$prob->src},
  966:                                            'csv','display',
  967:                                            @extra_resp_headers);
  968:                 $start_col{$prob->symb}->{$partid}->{$responses->[$i]}=
  969:                     $max_column;
  970:                 $headers{'response'}->[$max_column]=
  971:                     &mt('Response [_1]',$responses->[$i]);
  972:                 for (my $j=0;$j<=$#headers;$j++) {
  973:                     $headers{'student'}->[$max_column+$j]=$headers[$j];
  974:                 }
  975:                 $max_column += scalar(@headers);
  976:             }
  977:         }
  978:     }
  979:     foreach my $row ('problem','part','response','student') {
  980:         print $outputfile '"'.
  981:             join('","',
  982:                  map { 
  983:                      &Apache::loncommon::csv_translate($_); 
  984:                  } @{$headers{$row}}).'"'.$/;
  985:     }
  986:     #
  987:     # Main loop
  988:     foreach my $student (@$students) {
  989:         last if ($c->aborted());
  990:         my @rows;
  991:         foreach my $prob (@$problems) {
  992:             foreach my $partid (@{$prob->parts}) {
  993:                 my @responses = $prob->responseIds($partid);
  994:                 my @response_type = $prob->responseType($partid);
  995:                 for (my $i=0;$i<=$#responses;$i++) {
  996:                     my $respid   = $responses[$i];
  997:                     my $resptype = $response_type[$i];
  998:                     my @headers = &get_headers($prob,$partid,$respid,$resptype,
  999:                                                $problem_analysis{$prob->src},
 1000:                                                'csv','normal',
 1001:                                                @extra_resp_headers);
 1002:                     my $results = 
 1003:                         &Apache::loncoursedata::get_response_data_by_student
 1004:                         ($student,$prob->symb(),$respid);
 1005:                     if (! defined($results)) {
 1006:                         $results = [];
 1007:                     }
 1008:                     for (my $j=0; $j<scalar(@$results);$j++) {
 1009:                         if ($env{'form.all_sub'} ne 'true') {
 1010:                             next if ($j != 0);
 1011:                         }
 1012:                         my $idx = scalar(@$results) - $j - 1;
 1013:                         my $response = &hashify_response($results->[$idx],
 1014:                                                          $prob,$student,
 1015:                                                          $partid,$respid);
 1016:                         my @data = &compile_response_data(\@headers,$response,
 1017:                                                           $prob,$partid,
 1018:                                                           $respid,$resptype,
 1019:                                                           \&csv_format_item);
 1020:                         my $resp_start_idx =
 1021:                             $start_col{$prob->symb}->{$partid}->{$respid};
 1022:                         for (my $k=0;$k<=$#data;$k++) {
 1023:                             $rows[$j]->[$resp_start_idx + $k] = $data[$k];
 1024:                         }
 1025:                     }
 1026:                 }
 1027:             }
 1028:         }
 1029:         foreach my $row (@rows) {
 1030:             print $outputfile '"'.join('","',
 1031:                                        map { $student->{$_}; }
 1032:                                        @student_columns).'"';
 1033:             for (my $i=scalar(@student_columns);$i<$max_column;$i++) {
 1034:                 my $value = &Apache::loncommon::csv_translate($row->[$i]);
 1035:                 $value ||='';
 1036:                 print $outputfile ',"'.$value.'"';
 1037:             }
 1038:             print $outputfile $/;
 1039:         }
 1040:         undef(@rows);
 1041:         &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
 1042:                                                  'last student');
 1043:     }
 1044:     close($outputfile);
 1045:     #
 1046:     # Close the progress window
 1047:     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
 1048:     #
 1049:     # Tell the user where to get their csv file
 1050:     $r->print('<br />'.
 1051:               '<a href="'.$filename.'">'.&mt('Your csv file.').'</a>'."\n");
 1052:     $r->rflush();
 1053:     return;
 1054: }
 1055: 
 1056: sub csv_format_item {
 1057:     my ($item,$type) = @_;
 1058:     if ($type eq 'Time') {
 1059:         $item = localtime($item);
 1060:     }
 1061:     $item =&Apache::loncommon::csv_translate($item); 
 1062:     return $item;
 1063: }
 1064: 
 1065: #########################################################
 1066: #########################################################
 1067: ##
 1068: ##   Generic Interface Routines
 1069: ##
 1070: #########################################################
 1071: #########################################################
 1072: sub CreateInterface {
 1073:     ##
 1074:     ## Output Selection
 1075:     my $output_selector = $/.'<select name="output">'.$/;
 1076:     foreach ('HTML','Excel','CSV') {
 1077:         $output_selector .= '    <option value="'.lc($_).'"';
 1078:         if ($env{'form.output'} eq lc($_)) {
 1079:             $output_selector .= ' selected ';
 1080:         }
 1081:         $output_selector .='>'.&mt($_).'</option>'.$/;
 1082:     } 
 1083:     $output_selector .= '</select>'.$/;
 1084:     ##
 1085:     ## Environment variable initialization
 1086:     my $Str = '';
 1087:     $Str .= &Apache::lonhtmlcommon::breadcrumbs('Student Submission Reports');
 1088:     $Str .= '<p>';
 1089:     $Str .= '<table cellspacing="5">'."\n";
 1090:     $Str .= '<tr>';
 1091:     $Str .= '<th>'.&mt('Sections').'</th>';
 1092:     $Str .= '<th>'.&mt('Groups').'</th>';
 1093:     $Str .= '<th>'.&mt('Access Status').'</th>';
 1094:     $Str .= '<th>'.&mt('Output as [_1]',$output_selector).'</th>';
 1095:     $Str .= '</tr>'."\n";
 1096:     #
 1097:     $Str .= '<tr><td align="center">'."\n";
 1098:     $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
 1099:     $Str .= '</td>';
 1100:     #
 1101:     $Str .= '<td align="center">'."\n";
 1102:     $Str .= &Apache::lonstatistics::GroupSelect('Group','multiple',5);
 1103:     $Str .= '</td>';
 1104:     #
 1105:     $Str .= '<td align="center">';
 1106:     $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5);
 1107:     $Str .= '</td>';
 1108:     #
 1109:     # Render problem checkbox
 1110:     my $prob_checkbox = '<input type="checkbox" name="renderprob" ';
 1111:     if (exists($env{'form.renderprob'}) && $env{'form.renderprob'} eq 'true') {
 1112:         $prob_checkbox .= 'checked ';
 1113:     }
 1114:     $prob_checkbox .= 'value="true" />';
 1115:     #
 1116:     # Compute correct answers checkbox
 1117:     my $ans_checkbox = '<input type="checkbox" name="correctans" ';
 1118:     if (exists($env{'form.correctans'}) && $env{'form.correctans'} eq 'true') {
 1119:         $ans_checkbox .= 'checked ';
 1120:     }
 1121:     $ans_checkbox .= 'value="true" />';
 1122:     #
 1123:     # Show all submissions checkbox
 1124:     my $all_sub_checkbox = '<input type="checkbox" name="all_sub" ';
 1125:     if (exists($env{'form.all_sub'}) && 
 1126:         $env{'form.all_sub'} eq 'true') {
 1127:         $all_sub_checkbox .= 'checked ';
 1128:     }
 1129:     $all_sub_checkbox.= 'value="true" />';
 1130:     #
 1131:     # problem status checkbox
 1132:     my $prob_status_checkbox = '<input type="checkbox" name="prob_status" ';
 1133:     if (exists($env{'form.prob_status'}) && 
 1134:         $env{'form.prob_status'} eq 'true') {
 1135:         $prob_status_checkbox .= 'checked ';
 1136:     }
 1137:     $prob_status_checkbox .= 'value="true" />';
 1138:     #
 1139:     $Str .= '<td align="right" valign="top">'.
 1140:         '<label><b>'.
 1141:         &mt('Show problem [_1]',$prob_checkbox).'</b></label><br />'.
 1142:         '<label><b>'.
 1143:         &mt('Show correct answers [_1]',$ans_checkbox).'</b></label><br />'.
 1144:         '<label><b>'.
 1145:         &mt('Show all submissions [_1]',$all_sub_checkbox).
 1146:         '</b></label><br />'.
 1147:         '<label><b>'.
 1148:         &mt('Show problem grading [_1]',$prob_status_checkbox).
 1149:         '</b></label><br />'.
 1150:         '</td>';
 1151:     #
 1152:     $Str .= '</tr>'."\n";
 1153:     $Str .= '</table>'."\n";
 1154:     #
 1155:     $Str .= '<p><nobr>'.&mt('Status: [_1]',
 1156:                          '<input type="text" '.
 1157:                          'name="stats_status" size="60" value="" />').
 1158:             '</nobr>'.'</p>';    
 1159:     ##
 1160:     return $Str;
 1161: }
 1162: 
 1163: 1;
 1164: 
 1165: __END__

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