Diff for /loncom/interface/statistics/lonstathelpers.pm between versions 1.2 and 1.58

version 1.2, 2004/01/20 15:51:06 version 1.58, 2009/03/25 15:49:53
Line 40  routines that are needed across multiple Line 40  routines that are needed across multiple
   
 =head1 OVERVIEW  =head1 OVERVIEW
   
   
 =over 4  =over 4
   
 =cut  =cut
Line 50  routines that are needed across multiple Line 49  routines that are needed across multiple
 package Apache::lonstathelpers;  package Apache::lonstathelpers;
   
 use strict;  use strict;
 use Apache::lonnet();  use Apache::lonnet;
 use Apache::loncommon();  use Apache::loncommon();
 use Apache::lonhtmlcommon();  use Apache::lonhtmlcommon();
 use Apache::loncoursedata();  use Apache::loncoursedata();
Line 59  use Apache::lonlocal; Line 58  use Apache::lonlocal;
 use HTML::Entities();  use HTML::Entities();
 use Time::Local();  use Time::Local();
 use Spreadsheet::WriteExcel();  use Spreadsheet::WriteExcel();
   use GDBM_File;
   use Storable qw(freeze thaw);
   use lib '/home/httpd/lib/perl/';
   use LONCAPA;
    
   
 ####################################################  ####################################################
 ####################################################  ####################################################
Line 67  use Spreadsheet::WriteExcel(); Line 71  use Spreadsheet::WriteExcel();
   
 =item &render_resource($resource)  =item &render_resource($resource)
   
 Input: a resource generated from   Input: a navmaps resource
 &Apache::loncoursedata::get_sequence_assessment_data().  
   
 Retunrs: a scalar containing html for a rendering of the problem  Retunrs: a scalar containing html for a rendering of the problem
 within a table.  within a table.
Line 81  sub render_resource { Line 84  sub render_resource {
     my ($resource) = @_;      my ($resource) = @_;
     ##      ##
     ## Render the problem      ## Render the problem
     my $base;      my ($base) = ($resource->src =~ m|^(.*/)[^/]*$|);
     ($base,undef) = ($resource->{'src'} =~ m|(.*/)[^/]*$|);      $base="http://".$ENV{'SERVER_NAME'}.$base;
     $base = "http://".$ENV{'SERVER_NAME'}.$base;      my ($src,$symb)=($resource->link,&escape($resource->shown_symb));
     my $rendered_problem =       my $rendered_problem = &Apache::lonnet::ssi_body($src.'?symb='.$symb);
         &Apache::lonnet::ssi_body($resource->{'src'});  
     $rendered_problem =~ s/<\s*form\s*/<nop /g;      $rendered_problem =~ s/<\s*form\s*/<nop /g;
     $rendered_problem =~ s|(<\s*/form\s*>)|<\/nop>|g;      $rendered_problem =~ s|(<\s*/form\s*>)|<\/nop>|g;
     return '<table bgcolor="ffffff"><tr><td>'.      return '<table bgcolor="ffffff"><tr><td>'.
Line 99  sub render_resource { Line 101  sub render_resource {
   
 =pod  =pod
   
 =item &ProblemSelector($AcceptedResponseTypes)  =item &get_resources
   
   =cut
   
   ####################################################
   ####################################################
   sub get_resources {
       my ($navmap,$sequence) = @_;
       my @resources = $navmap->retrieveResources($sequence,
                                                  sub { shift->is_problem(); },
                                                  0,0,0);
       return @resources;
   }
   
   ####################################################
   ####################################################
   
   =pod
   
   =item &problem_selector($AcceptedResponseTypes)
   
 Input: scalar containing regular expression which matches response  Input: scalar containing regular expression which matches response
 types to show.  '.' will yield all, '(option|radiobutton)' will match  types to show.  '.' will yield all, '(option|radiobutton)' will match
Line 107  all option response and radiobutton prob Line 128  all option response and radiobutton prob
   
 Returns: A string containing html for a table which lists the sequences  Returns: A string containing html for a table which lists the sequences
 and their contents.  A radiobutton is provided for each problem.  and their contents.  A radiobutton is provided for each problem.
   Skips 'survey' problems.
   
 =cut  =cut
   
 ####################################################  ####################################################
 ####################################################  ####################################################
 sub ProblemSelector {  sub problem_selector {
     my ($AcceptedResponseTypes) = @_;      my ($AcceptedResponseTypes,$sequence_addendum) = @_;
     my $Str;      my $Str;
     $Str = "\n<table>\n";      $Str = "\n<table>\n";
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {      my $rb_count =0;
         next if ($seq->{'num_assess'}<1);      my ($navmap,@sequences) = 
           &Apache::lonstatistics::selected_sequences_with_assessments('all');
       return $navmap if (! ref($navmap)); # error
       foreach my $seq (@sequences) {
         my $seq_str = '';          my $seq_str = '';
         foreach my $res (@{$seq->{'contents'}}) {          foreach my $res (&get_resources($navmap,$seq)) {
             next if ($res->{'type'} ne 'assessment');              foreach my $part (@{$res->parts}) {
             foreach my $part (@{$res->{'parts'}}) {                  my @response_ids   = $res->responseIds($part);
                 my $partdata = $res->{'partdata'}->{$part};                  my @response_types = $res->responseType($part);
                 if ((! exists($partdata->{'option'}) ||                   for (my $i=0;$i<scalar(@response_types);$i++){
                      $partdata->{'option'} == 0      ) &&                      my $respid = $response_ids[$i];
                     (! exists($partdata->{'radiobutton'}) ||                      my $resptype = $response_types[$i];
                      $partdata->{'radiobutton'} == 0)) {  
                     next;  
                 }  
                 for (my $i=0;$i<scalar(@{$partdata->{'ResponseTypes'}});$i++){  
                     my $respid = $partdata->{'ResponseIds'}->[$i];  
                     my $resptype = $partdata->{'ResponseTypes'}->[$i];  
                     if ($resptype =~ m/$AcceptedResponseTypes/) {                      if ($resptype =~ m/$AcceptedResponseTypes/) {
                         my $value = &make_target_id({symb=>$res->{'symb'},                          my $value = &make_target_id({symb=>$res->symb,
                                                      part=>$part,                                                       part=>$part,
                                                      respid=>$respid,                                                       respid=>$respid,
                                                      resptype=>$resptype});                                                       resptype=>$resptype});
                         my $checked = '';                          my $checked = '';
                         if ($ENV{'form.problemchoice'} eq $value) {                          if ($env{'form.problemchoice'} eq $value) {
                             $checked = 'checked ';                              $checked = 'checked="checked" ';
                         }                          }
                         my $title = $res->{'title'};                          my $title = $res->compTitle;
                         if (! defined($title) || $title eq '') {                          if (! defined($title) || $title eq '') {
                             ($title) = ($res->{'src'} =~ m:/([^/]*)$:);                              ($title) = ($res->src =~ m:/([^/]*)$:);
                         }                          }
                         $seq_str .= '<tr><td>'.                          $seq_str .= '<tr>'.
   '<input type="radio" name="problemchoice" value="'.$value.'" '.$checked.'/>'.                              qq{<td><input type="radio" id="$rb_count" name="problemchoice" value="$value" $checked /></td>}.
   '</td><td>'.                                        '<td><label for="'.$rb_count.'">'.$resptype.'</label></td>'.
   $resptype.'</td><td>'.                              '<td><label for="'.$rb_count.'">'.$title.'</label>';
   '<a href="'.$res->{'src'}.'">'.$title.'</a> ';                          if (scalar(@response_ids) > 1) {
 #  '<a href="'.$res->{'src'}.'">'.$resptype.' '.$res->{'title'}.'</a> ';  
                         if ($partdata->{'option'} > 1) {  
                             $seq_str .= &mt('response').' '.$respid;                              $seq_str .= &mt('response').' '.$respid;
                         }                          }
                           my $link = $res->link.'?symb='.&escape($res->shown_symb);
                           $seq_str .= ('&nbsp;'x2).
                               '<a target="preview" href="'.$link.'">'.&mt('view').'</a>';
                         $seq_str .= "</td></tr>\n";                          $seq_str .= "</td></tr>\n";
                           $rb_count++;
                     }                      }
                 }                  }
             }              }
         }          }
         if ($seq_str ne '') {          if ($seq_str ne '') {
             $Str .= '<tr><td>&nbsp</td><td colspan="2"><b>'.$seq->{'title'}.'</b></td>'.              $Str .= '<tr><td>&nbsp;</td>'.
                   '<td colspan="2"><b>'.$seq->compTitle.'</b></td>'.
                 "</tr>\n".$seq_str;                  "</tr>\n".$seq_str;
               if (defined($sequence_addendum)) {
                   $Str .= '<tr>'.
                       ('<td>&nbsp;</td>'x2).
                       '<td align="right">'.$sequence_addendum.'</td>'.
                       "</tr>\n";
               }
         }          }
     }      }
     $Str .= "</table>\n";      $Str .= "</table>\n";
Line 173  sub ProblemSelector { Line 201  sub ProblemSelector {
   
 =pod  =pod
   
   =item &MultipleProblemSelector($navmap,$selected,$inputname)
   
   Generate HTML with checkboxes for problem selection.
   
   Input: 
   
   $navmap: a navmap object.  If undef, navmaps will be called to create a
   new object.
   
   $selected: Scalar, Array, or hash reference of currently selected items.
   
   $inputname: The name of the form elements to use for the checkboxs.
   
   Returns: A string containing html for a table which lists the sequences
   and their contents.  A checkbox is provided for each problem.
   
   =cut
   
   ####################################################
   ####################################################
   sub MultipleProblemSelector {
       my ($navmap,$inputname,$formname)=@_;
       my $cid = $env{'request.course.id'};
       my $Str;
       # Massage the input as needed.
       if (! defined($navmap)) {
           $navmap = Apache::lonnavmaps::navmap->new();
           if (! defined($navmap)) {
               $Str .= '<div class="LC_error">'
                      .&mt('Error: cannot process course structure')
                      .'</div>';
               return $Str;
           }
       }
       my $selected = {map { ($_,1) } (&get_selected_symbs($inputname))};
       # Header
       $Str .= <<"END";
   <script type="text/javascript" language="JavaScript">
       function checkall(value,seqid) {
           for (i=0; i<document.forms.$formname.elements.length; i++) {
               ele = document.forms.$formname.elements[i];
               if (ele.name == '$inputname') {
                   if (seqid != null) {
                       itemid = document.forms.$formname.elements[i].id;
                       thing = itemid.split(':');
                       if (thing[0] == seqid) {
                           document.forms.$formname.elements[i].checked=value;
                       }
                   } else {
                       document.forms.$formname.elements[i].checked=value;
                   }
               }
           }
       }
   </script>
   END
       $Str .= 
           '<a href="javascript:checkall(true)">'.&mt('Select All').'</a>'.
           ('&nbsp;'x4).
           '<a href="javascript:checkall(false)">'.&mt('Unselect All').'</a>';
       $Str .= $/.'<table>'.$/;
       my $iterator = $navmap->getIterator(undef, undef, undef, 1);
       my $sequence_string;
       my $seq_id = 0;
       my @Accumulator = (&new_accumulator($env{'course.'.$cid.'.description'},
                                           '',
                                           '',
                                           $seq_id++,
                                           $inputname));
       my @Sequence_Data;
       while (my $curRes = $iterator->next()) {
           if ($curRes == $iterator->END_MAP) {
               if (ref($Accumulator[-1]) eq 'CODE') {
                   my $old_accumulator = pop(@Accumulator);
                   push(@Sequence_Data,&{$old_accumulator}());
               }
           } elsif ($curRes == $iterator->BEGIN_MAP) {
               # Not much to do here.
           }
           next if (! ref($curRes));
           if ($curRes->is_map) {
               push(@Accumulator,&new_accumulator($curRes->compTitle,
                                                  $curRes->src,
                                                  $curRes->symb,
                                                  $seq_id++,
                                                  $inputname));
           } elsif ($curRes->is_problem) {
               if (@Accumulator && $Accumulator[-1] ne '') {
                   &{$Accumulator[-1]}($curRes,
                                       exists($selected->{$curRes->symb}));
               }
           }
       }
       my $course_seq = pop(@Sequence_Data);
       foreach my $seq ($course_seq,@Sequence_Data) {
           #my $seq = pop(@Sequence_Data);
           next if (! defined($seq) || ref($seq) ne 'HASH');
           $Str.= '<tr><td colspan="2">'.
               '<b>'.$seq->{'title'}.'</b>'.('&nbsp;'x2).
               '<a href="javascript:checkall(true,'.$seq->{'id'}.')">'.
                                     &mt('Select').'</a>'.('&nbsp;'x2).
               '<a href="javascript:checkall(false,'.$seq->{'id'}.')">'.
                                     &mt('Unselect').'</a>'.('&nbsp;'x2).
               '</td></tr>'.$/;
           $Str.= $seq->{'html'};
       }
       $Str .= '</table>'.$/;
       return $Str;
   }
   
   sub new_accumulator {
       my ($title,$src,$symb,$seq_id,$inputname) = @_;
       my $target;
       my $item_id=0;
       return 
           sub {
               if (@_) { 
                   my ($res,$checked) = @_;
                   $target.='<tr><td><label>'.
                       '<input type="checkbox" name="'.$inputname.'" ';
                   if ($checked) {
                       $target .= 'checked="checked" ';
                   }
                   $target .= 'id="'.$seq_id.':'.$item_id++.'" ';
                   $target.= 
                       'value="'.&escape($res->symb).'" />'.
                       '&nbsp;'.$res->compTitle.'</label>'.
                       ('&nbsp;'x2).'<a target="preview" '.
                       'href="'.$res->link.'?symb='.
                       &escape($res->shown_symb).'">'.&mt('view').'</a>'.
                       '</td></tr>'.$/;
               } else { 
                   if (defined($target)) {
                       return { title => $title,
                                symb  => $symb,
                                src   => $src,
                                id    => $seq_id,
                                html  => $target, }; 
                   }
                   return undef;
               }
           };
   }
   
   sub get_selected_symbs {
       my ($inputfield) = @_;
       my $field = 'form.'.$inputfield;
       my @symbs = (map {
                        &unescape($_);
                        } &Apache::loncommon::get_env_multiple($field));
       return @symbs;
   }
   
   ####################################################
   ####################################################
   
   =pod
   
 =item &make_target_id($target)  =item &make_target_id($target)
   
 Inputs: Hash ref with the following entries:  Inputs: Hash ref with the following entries:
Line 190  Used by Apache::lonstathelpers::ProblemS Line 376  Used by Apache::lonstathelpers::ProblemS
 ####################################################  ####################################################
 sub make_target_id {  sub make_target_id {
     my ($target) = @_;      my ($target) = @_;
     my $id = &Apache::lonnet::escape($target->{'symb'}).':'.      my $id = &escape($target->{'symb'}).':'.
              &Apache::lonnet::escape($target->{'part'}).':'.               &escape($target->{'part'}).':'.
              &Apache::lonnet::escape($target->{'respid'}).':'.               &escape($target->{'respid'}).':'.
              &Apache::lonnet::escape($target->{'resptype'});               &escape($target->{'resptype'});
     return $id;      return $id;
 }  }
   
Line 216  Returns: A hash reference, $target, cont Line 402  Returns: A hash reference, $target, cont
 ####################################################  ####################################################
 sub get_target_from_id {  sub get_target_from_id {
     my ($id) = @_;      my ($id) = @_;
     my ($symb,$part,$respid,$resptype) = split(':',$id);      if (! ref($id)) {
     return ({ symb    =>&Apache::lonnet::unescape($symb),          my ($symb,$part,$respid,$resptype) = split(':',$id);
              part     =>&Apache::lonnet::unescape($part),          return ({ symb     => &unescape($symb),
              respid   =>&Apache::lonnet::unescape($respid),                    part     => &unescape($part),
              resptype =>&Apache::lonnet::unescape($resptype)});                    respid   => &unescape($respid),
                     resptype => &unescape($resptype)});
       } elsif (ref($id) eq 'ARRAY') {
           my @Return;
           foreach my $selected (@$id) {
               my ($symb,$part,$respid,$resptype) = split(':',$selected);
               push(@Return,{ symb     => &unescape($symb),
                              part     => &unescape($part),
                              respid   => &unescape($respid),
                              resptype => &unescape($resptype)});
           }
           return \@Return;
       }
 }  }
   
 ####################################################  ####################################################
Line 228  sub get_target_from_id { Line 426  sub get_target_from_id {
   
 =pod  =pod
   
 =item &get_prev_curr_next($target)  =item &get_prev_curr_next($target,$AcceptableResponseTypes,$granularity)
   
 Determine the problem parts or responses preceeding and following the  Determine the problem parts or responses preceeding and following the
 current resource.  current resource.
Line 236  current resource. Line 434  current resource.
 Inputs: $target (see &Apache::lonstathelpers::get_target_from_id())  Inputs: $target (see &Apache::lonstathelpers::get_target_from_id())
   $AcceptableResponseTypes, regular expression matching acceptable    $AcceptableResponseTypes, regular expression matching acceptable
                             response types,                              response types,
   $granularity, either 'part' or 'response'    $granularity, either 'part', 'response', 'part_survey', or 'part_task'
   
 Returns: three hash references, $prev, $curr, $next, which refer to the  Returns: three hash references, $prev, $curr, $next, which refer to the
 preceeding, current, or following problem parts or responses, depending  preceeding, current, or following problem parts or responses, depending
Line 259  sub get_prev_curr_next { Line 457  sub get_prev_curr_next {
     #      #
     # Build an array with the data we need to search through      # Build an array with the data we need to search through
     my @Resource;      my @Resource;
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {      my ($navmap,@sequences) = 
         foreach my $res (@{$seq->{'contents'}}) {          &Apache::lonstatistics::selected_sequences_with_assessments('all');
             next if ($res->{'type'} ne 'assessment');      return $navmap if (! ref($navmap));
             foreach my $part (@{$res->{'parts'}}) {      foreach my $seq (@sequences) {
                 my $partdata = $res->{'partdata'}->{$part};          my @resources = &get_resources($navmap,$seq);
                 if ($granularity eq 'part') {          foreach my $res (@resources) {
               foreach my $part (@{$res->parts}) {
                   if ($res->is_survey($part) && ($granularity eq 'part_survey')){
                       push (@Resource,
                             { symb     => $res->symb,
                               part     => $part,
                               resource => $res,
                           } );
    } elsif ($res->is_task($part) && ($granularity eq 'part_task')){
                     push (@Resource,                      push (@Resource,
                           { symb     => $res->{symb},                            { symb     => $res->symb,
                               part     => $part,
                               resource => $res,
                           } );
                   } elsif ($granularity eq 'part') {
                       push (@Resource,
                             { symb     => $res->symb,
                             part     => $part,                              part     => $part,
                             resource => $res,                              resource => $res,
                         } );                          } );
                 } elsif ($granularity eq 'response') {                  } elsif ($granularity eq 'response') {
                       my @response_ids   = $res->responseIds($part);
                       my @response_types = $res->responseType($part);
                     for (my $i=0;                      for (my $i=0;
                          $i<scalar(@{$partdata->{'ResponseTypes'}});                           $i<scalar(@response_ids);
                          $i++){                           $i++){
                         my $respid = $partdata->{'ResponseIds'}->[$i];                          my $respid   = $response_ids[$i];
                         my $resptype = $partdata->{'ResponseTypes'}->[$i];                          my $resptype = $response_types[$i];
                         next if ($resptype !~ m/$AcceptableResponseTypes/);                          next if ($resptype !~ m/$AcceptableResponseTypes/);
                         push (@Resource,                          push (@Resource,
                               { symb     => $res->{symb},                                { symb     => $res->symb,
                                 part     => $part,                                  part     => $part,
                                 respid   => $partdata->{'ResponseIds'}->[$i],                                  respid   => $respid,
                                   resptype => $resptype,
                                 resource => $res,                                  resource => $res,
                                 resptype => $resptype  
                                 } );                                  } );
                     }                      }
                 }                  }
Line 294  sub get_prev_curr_next { Line 508  sub get_prev_curr_next {
     my $curr_idx;      my $curr_idx;
     for ($curr_idx=0;$curr_idx<$#Resource;$curr_idx++) {      for ($curr_idx=0;$curr_idx<$#Resource;$curr_idx++) {
         my $curr_item = $Resource[$curr_idx];          my $curr_item = $Resource[$curr_idx];
         if ($granularity eq 'part') {          if ($granularity =~ /^(part|part_survey|part_task)$/) {
             if ($curr_item->{'symb'} eq $target->{'symb'} &&              if ($curr_item->{'symb'} eq $target->{'symb'} &&
                 $curr_item->{'part'} eq $target->{'part'}) {                  $curr_item->{'part'} eq $target->{'part'}) {
                 last;                  last;
Line 309  sub get_prev_curr_next { Line 523  sub get_prev_curr_next {
         }          }
     }      }
     my $curr_item = $Resource[$curr_idx];      my $curr_item = $Resource[$curr_idx];
     if ($granularity eq 'part') {      if ($granularity =~ /^(part|part_survey|part_task)$/) {
         if ($curr_item->{'symb'}     ne $target->{'symb'} ||          if ($curr_item->{'symb'}     ne $target->{'symb'} ||
             $curr_item->{'part'}     ne $target->{'part'}) {              $curr_item->{'part'}     ne $target->{'part'}) {
             # bogus symb - return nothing              # bogus symb - return nothing
Line 340  sub get_prev_curr_next { Line 554  sub get_prev_curr_next {
         $curr = $Resource[$curr_idx  ];          $curr = $Resource[$curr_idx  ];
         $next = $Resource[$curr_idx+1];          $next = $Resource[$curr_idx+1];
     }      }
     return ($prev,$curr,$next);      return ($navmap,$prev,$curr,$next);
   }
   
   
   #####################################################
   #####################################################
   
   =pod
   
   =item GetStudentAnswers($r,$problem,$Students)
   
   Determines the correct answer for a set of students on a given problem.
   The students answers are stored in the student hashes pointed to by the
   array @$Students under the key 'answer'.
   
   Inputs: $r
   $problem: hash reference containing the keys 'resource', 'part', and 'respid'.
   $Students: reference to array containing student hashes (need 'username', 
       'domain').  
   
   Returns: nothing 
   
   =cut
   
   #####################################################
   #####################################################
   sub GetStudentAnswers {
       my ($r,$problem,$Students,$formname,$inputname) = @_;
       my %answers;
       my $status_type;
       if (defined($formname)) {
           $status_type = 'inline';
       } else {
           $status_type = 'popup';
       }    
       my $c = $r->connection();
       my %Answers;
       my ($resource,$partid,$respid) = ($problem->{'resource'},
                                         $problem->{'part'},
                                         $problem->{'respid'});
       # Read in the cache (if it exists) before we start timing things.
       &Apache::lonstathelpers::ensure_proper_cache($resource->{'symb'});
       # Open progress window
       my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
           ($r,'Student Answer Compilation Status',
            'Student Answer Compilation Progress', scalar(@$Students),
            $status_type,undef,$formname,$inputname);
       $r->rflush();
       foreach my $student (@$Students) {
           last if ($c->aborted());
           my $sname = $student->{'username'};
           my $sdom = $student->{'domain'};
           my $answer = &Apache::lonstathelpers::get_student_answer
               ($resource,$sname,$sdom,$partid,$respid);
           &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
                                                    &mt('last student'));
           $answers{$answer}++;
           $student->{'answer'} = $answer;
       }
       &Apache::lonstathelpers::write_analysis_cache();
       return if ($c->aborted());
       $r->rflush();
       # close progress window
       &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
       return \%answers;
   }
   
   #####################################################
   #####################################################
   
   =pod
   
   =item analyze_problem_as_student
   
   Analyzes a homework problem for a student
   
   Inputs: $resource: a resource object
           $sname, $sdom, $partid, $respid
   
   Returns: the problem analysis hash
   
   =cut
   
   #####################################################
   #####################################################
   sub analyze_problem_as_student {
       my ($resource,$sname,$sdom) = @_;
       if (ref($resource) ne 'HASH') {
           my $res = $resource;
           $resource = { 'src' => $res->src,
                         'symb' => $res->symb,
                         'parts' => $res->parts };
           foreach my $part (@{$resource->{'parts'}}) {
               $resource->{'partdata'}->{$part}->{'ResponseIds'}=
                   [$res->responseIds($part)];
           }
       }
       my $url = $resource->{'src'};
       my $symb = $resource->{'symb'};
       my $analysis = &get_from_analysis_cache($sname,$sdom,$symb);
       if (! defined($analysis)) {
           my $courseid = $env{'request.course.id'};
           my $Answ=&Apache::lonnet::ssi($url,('grade_target' => 'analyze',
                                               'grade_domain' => $sdom,
                                               'grade_username' => $sname,
                                               'grade_symb' => $symb,
                                               'grade_courseid' => $courseid));
           (my $garbage,$analysis)=split(/_HASH_REF__/,$Answ,2);
           &store_analysis($sname,$sdom,$symb,$analysis);
       }
       my %Answer=&Apache::lonnet::str2hash($analysis);
       #
       return \%Answer;
   }
   
   #####################################################
   #####################################################
   
   =pod
   
   =item get_student_answer
   
   Analyzes a homework problem for a particular student and returns the correct 
   answer.  Attempts to put together an answer for problem types 
   that do not natively support it.
   
   Inputs: $resource: a resource object (from navmaps or hash from loncoursedata)
           $sname, $sdom, $partid, $respid
   
   Returns: $answer
   
   If $partid and $respid are specified, $answer is simply a scalar containing
   the correct answer for the response.
   
   If $partid or $respid are undefined, $answer will be a hash reference with
   keys $partid.'.'.$respid.'.answer'.
   
   =cut
   
   #####################################################
   #####################################################
   sub get_student_answer {
       my ($resource,$sname,$sdom,$partid,$respid) = @_;
       #
       if (ref($resource) ne 'HASH') {
           my $res = $resource;
           $resource = { 'src' => $res->src,
                         'symb' => $res->symb,
                         'parts' => $res->parts };
           foreach my $part (@{$resource->{'parts'}}) {
               $resource->{'partdata'}->{$part}->{'ResponseIds'}=
                   [$res->responseIds($part)];
           }
       }
       #
       my $analysis = 
           &analyze_problem_as_student($resource,$sname,$sdom);
       my $answer;
       foreach my $partid (@{$resource->{'parts'}}) {
           my $partdata = $resource->{'partdata'}->{$partid};
           foreach my $respid (@{$partdata->{'ResponseIds'}}) {
               my $prefix = $partid.'.'.$respid;
               my $key = $prefix.'.answer';
               $answer->{$partid}->{$respid} = 
                   &get_answer($prefix,$key,%$analysis);
           }
       }
       my $returnvalue;
       if (! defined($partid)) {
           $returnvalue = $answer;
       } elsif (! defined($respid)) {
           $returnvalue = $answer->{$partid};
       } else {
           $returnvalue = $answer->{$partid}->{$respid};
       }
       return $returnvalue;
   }
   
   sub get_answer {
       my ($prefix,$key,%Answer) = @_;
       my $returnvalue;
       if (exists($Answer{$key})) {
    if (ref($Answer{$key}) eq 'HASH') {
       my $which = 'INTERNAL';
       if (!exists($Answer{$key}{$which})) {
    $which = (sort(keys(%{ $Answer{$key} })))[0];
       }
       my $student_answer = $Answer{$key}{$which}[0][0];
       $returnvalue = $student_answer; 
    } else {
       &Apache::lonnet::logthis("error analyzing problem. got a answer of type ".ref($Answer{$key}));
    }
       } else {
           if (exists($Answer{$prefix.'.shown'})) {
               # The response has foils
               my %values;
               while (my ($k,$v) = each(%Answer)) {
                   next if ($k !~ /^$prefix\.foil\.(value|area)\.(.*)$/);
                   my $foilname = $2;
                   $values{$foilname}=$v;
               }
               foreach my $foil (@{$Answer{$prefix.'.shown'}}) {
                   if (ref($values{$foil}) eq 'ARRAY') {
                       $returnvalue.=&HTML::Entities::encode($foil,'<>&"').'='.
                           join(',',map {&HTML::Entities::encode($_,'<>&"')} @{$values{$foil}}).'&';
                   } else {
                       $returnvalue.=&HTML::Entities::encode($foil,'<>&"').'='.
                           &HTML::Entities::encode($values{$foil},'<>&"').'&';
                   }
               }
               $returnvalue =~ s/ /\%20/g;
               chop ($returnvalue);
           }
       }
       return $returnvalue;
   }
   
   #####################################################
   #####################################################
   
   =pod
   
   =item Caching routines
   
   =over 4
   
   =item &load_analysis_cache($symb)
   
   Loads the cache for the given symb into memory from disk.  
   Requires the cache filename be set.  
   Only should be called by &ensure_proper_cache.
   
   =cut
   
   #####################################################
   #####################################################
   {
       my $cache_filename = undef;
       my $current_symb = undef;
       my %cache;
   
   sub load_analysis_cache {
       my ($symb) = @_;
       return if (! defined($cache_filename));
       if (! defined($current_symb) || $current_symb ne $symb) {
           undef(%cache);
           my $storedstring;
           my %cache_db;
           if (tie(%cache_db,'GDBM_File',$cache_filename,&GDBM_READER(),0640)) {
               $storedstring = $cache_db{&escape($symb)};
               untie(%cache_db);
           }
           if (defined($storedstring)) {
               %cache = %{thaw($storedstring)};
           }
       }
       return;
   }
   
   #####################################################
   #####################################################
   
   =pod
   
   =item &get_from_analysis_cache($sname,$sdom,$symb,$partid,$respid)
   
   Returns the appropriate data from the cache, or undef if no data exists.
   
   =cut
   
   #####################################################
   #####################################################
   sub get_from_analysis_cache {
       my ($sname,$sdom,$symb) = @_;
       &ensure_proper_cache($symb);
       my $returnvalue;
       if (exists($cache{$sname.':'.$sdom})) {
           $returnvalue = $cache{$sname.':'.$sdom};
       } else {
           $returnvalue = undef;
       }
       return $returnvalue;
   }
   
   #####################################################
   #####################################################
   
   =pod
   
   =item &write_analysis_cache($symb)
   
   Writes the in memory cache to disk so that it can be read in with
   &load_analysis_cache($symb).
   
   =cut
   
   #####################################################
   #####################################################
   sub write_analysis_cache {
       return if (! defined($current_symb) || ! defined($cache_filename));
       my %cache_db;
       my $key = &escape($current_symb);
       if (tie(%cache_db,'GDBM_File',$cache_filename,&GDBM_WRCREAT(),0640)) {
           my $storestring = freeze(\%cache);
           $cache_db{$key}=$storestring;
           $cache_db{$key.'.time'}=time;
           untie(%cache_db);
       }
       undef(%cache);
       undef($current_symb);
       undef($cache_filename);
       return;
   }
   
   #####################################################
   #####################################################
   
   =pod
   
   =item &ensure_proper_cache($symb)
   
   Called to make sure we have the proper cache set up.  This is called
   prior to every analysis lookup.
   
   =cut
   
   #####################################################
   #####################################################
   sub ensure_proper_cache {
       my ($symb) = @_;
       my $cid = $env{'request.course.id'};
       my $new_filename =  '/home/httpd/perl/tmp/'.
           'problemanalysis_'.$cid.'_analysis_cache.db';
       if (! defined($cache_filename) ||
           $cache_filename ne $new_filename ||
           ! defined($current_symb)   ||
           $current_symb ne $symb) {
           $cache_filename = $new_filename;
           # Notice: $current_symb is not set to $symb until after the cache is
           # loaded.  This is what tells &load_analysis_cache to load in a new
           # symb cache.
           &load_analysis_cache($symb);
           $current_symb = $symb;
       }
   }
   
   #####################################################
   #####################################################
   
   =pod
   
   =item &store_analysis($sname,$sdom,$symb,$partid,$respid,$dataset)
   
   Stores the analysis data in the in memory cache.
   
   =cut
   
   #####################################################
   #####################################################
   sub store_analysis {
       my ($sname,$sdom,$symb,$dataset) = @_;
       return if ($symb ne $current_symb);
       $cache{$sname.':'.$sdom}=$dataset;
       return;
   }
   
   }
   #####################################################
   #####################################################
   
   =pod
   
   =back
   
   =cut
   
   #####################################################
   #####################################################
   
   ##
   ## The following is copied from datecalc1.pl, part of the 
   ## Spreadsheet::WriteExcel CPAN module.
   ##
   ##
   ######################################################################
   #
   # Demonstration of writing date/time cells to Excel spreadsheets,
   # using UNIX/Perl time as source of date/time.
   #
   # Copyright 2000, Andrew Benham, adsb@bigfoot.com
   #
   ######################################################################
   #
   # UNIX/Perl time is the time since the Epoch (00:00:00 GMT, 1 Jan 1970)
   # measured in seconds.
   #
   # An Excel file can use exactly one of two different date/time systems.
   # In these systems, a floating point number represents the number of days
   # (and fractional parts of the day) since a start point. The floating point
   # number is referred to as a 'serial'.
   # The two systems ('1900' and '1904') use different starting points:
   #  '1900'; '1.00' is 1 Jan 1900 BUT 1900 is erroneously regarded as
   #          a leap year - see:
   #            http://support.microsoft.com/support/kb/articles/Q181/3/70.asp
   #          for the excuse^H^H^H^H^H^Hreason.
   #  '1904'; '1.00' is 2 Jan 1904.
   #
   # The '1904' system is the default for Apple Macs. Windows versions of
   # Excel have the option to use the '1904' system.
   #
   # Note that Visual Basic's "DateSerial" function does NOT erroneously
   # regard 1900 as a leap year, and thus its serials do not agree with
   # the 1900 serials of Excel for dates before 1 Mar 1900.
   #
   # Note that StarOffice (at least at version 5.2) does NOT erroneously
   # regard 1900 as a leap year, and thus its serials do not agree with
   # the 1900 serials of Excel for dates before 1 Mar 1900.
   #
   ######################################################################
   #
   # Calculation description
   # =======================
   #
   # 1900 system
   # -----------
   # Unix time is '0' at 00:00:00 GMT 1 Jan 1970, i.e. 70 years after 1 Jan 1900.
   # Of those 70 years, 17 (1904,08,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68)
   # were leap years with an extra day.
   # Thus there were 17 + 70*365 days = 25567 days between 1 Jan 1900 and
   # 1 Jan 1970.
   # In the 1900 system, '1' is 1 Jan 1900, but as 1900 was not a leap year
   # 1 Jan 1900 should really be '2', so 1 Jan 1970 is '25569'.
   #
   # 1904 system
   # -----------
   # Unix time is '0' at 00:00:00 GMT 1 Jan 1970, i.e. 66 years after 1 Jan 1904.
   # Of those 66 years, 17 (1904,08,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68)
   # were leap years with an extra day.
   # Thus there were 17 + 66*365 days = 24107 days between 1 Jan 1904 and
   # 1 Jan 1970.
   # In the 1904 system, 2 Jan 1904 being '1', 1 Jan 1970 is '24107'.
   #
   ######################################################################
   #
   # Copyright (c) 2000, Andrew Benham.
   # This program is free software. It may be used, redistributed and/or
   # modified under the same terms as Perl itself.
   #
   # Andrew Benham, adsb@bigfoot.com
   # London, United Kingdom
   # 11 Nov 2000
   #
   ######################################################################
   #-----------------------------------------------------------
   # calc_serial()
   #
   # Called with (up to) 2 parameters.
   #   1.  Unix timestamp.  If omitted, uses current time.
   #   2.  GMT flag. Set to '1' to return serial in GMT.
   #       If omitted, returns serial in appropriate timezone.
   #
   # Returns date/time serial according to $DATE_SYSTEM selected
   #-----------------------------------------------------------
   sub calc_serial {
       # Use 1900 date system on all platforms other than Apple Mac (for which
       # use 1904 date system).
       my $DATE_SYSTEM = ($^O eq 'MacOS') ? 1 : 0;
       my $time = (defined $_[0]) ? $_[0] : time();
       my $gmtflag = (defined $_[1]) ? $_[1] : 0;
       #
       # Divide timestamp by number of seconds in a day.
       # This gives a date serial with '0' on 1 Jan 1970.
       my $serial = $time / 86400;
       #
       # Adjust the date serial by the offset appropriate to the
       # currently selected system (1900/1904).
       if ($DATE_SYSTEM == 0) {        # use 1900 system
           $serial += 25569;
       } else {                        # use 1904 system
           $serial += 24107;
       }
       #
       unless ($gmtflag) {
           # Now have a 'raw' serial with the right offset. But this
           # gives a serial in GMT, which is false unless the timezone
           # is GMT. We need to adjust the serial by the appropriate
           # timezone offset.
           # Calculate the appropriate timezone offset by seeing what
           # the differences between localtime and gmtime for the given
           # time are.
           #    
           my @gmtime = gmtime($time);
           my @ltime  = localtime($time);
           #
           # For the first 7 elements of the two arrays, adjust the
           # date serial where the elements differ.
           for (0 .. 6) {
               my $diff = $ltime[$_] - $gmtime[$_];
               if ($diff) {
                   $serial += _adjustment($diff,$_);
               }
           }
       }
       #
       # Perpetuate the error that 1900 was a leap year by decrementing
       # the serial if we're using the 1900 system and the date is prior to
       # 1 Mar 1900. This has the effect of making serial value '60'
       # 29 Feb 1900.
       #
       # This fix only has any effect if UNIX/Perl time on the platform
       # can represent 1900. Many can't.
       #
       unless ($DATE_SYSTEM) {
           $serial-- if ($serial < 61);    # '61' is 1 Mar 1900
       }
       return $serial;
   }
   
   sub _adjustment {
       # Based on the difference in the localtime/gmtime array elements
       # number, return the adjustment required to the serial.
       #
       # We only look at some elements of the localtime/gmtime arrays:
       #    seconds    unlikely to be different as all known timezones
       #               have an offset of integral multiples of 15 minutes,
       #               but it's easy to do.
       #    minutes    will be different for timezone offsets which are
       #               not an exact number of hours.
       #    hours      very likely to be different.
       #    weekday    will differ when localtime/gmtime difference
       #               straddles midnight.
       #
       # Assume that difference between localtime and gmtime is less than
       # 5 days, then don't have to do maths for day of month, month number,
       # year number, etc...
       #
       my ($delta,$element) = @_;
       my $adjust = 0;
       #
       if ($element == 0) {            # Seconds
           $adjust = $delta/86400;         # 60 * 60 * 24
       } elsif ($element == 1) {       # Minutes
           $adjust = $delta/1440;          # 60 * 24
       } elsif ($element == 2) {       # Hours
           $adjust = $delta/24;            # 24
       } elsif ($element == 6) {       # Day of week number
           # Catch difference straddling Sat/Sun in either direction
           $delta += 7 if ($delta < -4);
           $delta -= 7 if ($delta > 4);
           #    
           $adjust = $delta;
       }
       return $adjust;
   }
   
   ###########################################################
   ###########################################################
   
   =pod
   
   =item get_problem_data
   
   Returns a data structure describing the problem.
   
   Inputs: $url
   
   Returns: %Partdata
   
   =cut
   
   ## note: we must force each foil and option to not begin or end with
   ##       spaces as they are stored without such data.
   ##
   ###########################################################
   ###########################################################
   sub get_problem_data {
       my ($url) = @_;
       my $Answ=&Apache::lonnet::ssi($url,('grade_target' => 'analyze'));
       (my $garbage,$Answ)=split(/_HASH_REF__/,$Answ,2);
       my %Answer;
       %Answer=&Apache::lonnet::str2hash($Answ);
       my %Partdata;
       foreach my $part (@{$Answer{'parts'}}) {
           while (my($key,$value) = each(%Answer)) {
               #
               # Logging code:
               if (0) {
                   &Apache::lonnet::logthis($part.' got key "'.$key.'"');
                   if (ref($value) eq 'ARRAY') {
                       &Apache::lonnet::logthis('    @'.join(',',@$value));
                   } else {
                       &Apache::lonnet::logthis('    '.$value);
                   }
               }
               # End of logging code
               next if ($key !~ /^\Q$part\E/);
               $key =~ s/^\Q$part\E\.//;
               if (ref($value) eq 'ARRAY') {
                   if ($key eq 'options') {
                       $Partdata{$part}->{'_Options'}=$value;
                   } elsif ($key eq 'concepts') {
                       $Partdata{$part}->{'_Concepts'}=$value;
                   } elsif ($key eq 'items') {
                       $Partdata{$part}->{'_Items'}=$value;
                   } elsif ($key =~ /^concept\.(.*)$/) {
                       my $concept = $1;
                       foreach my $foil (@$value) {
                           $Partdata{$part}->{'_Foils'}->{$foil}->{'_Concept'}=
                                                                         $concept;
                       }
                   } elsif ($key =~ /^(unit|incorrect|answer|ans_low|ans_high|str_type)$/) {
                       $Partdata{$part}->{$key}=$value;
                   }
               } else {
                   if ($key=~ /^foil\.text\.(.*)$/) {
                       my $foil = $1;
                       $Partdata{$part}->{'_Foils'}->{$foil}->{'name'}=$foil;
                       $value =~ s/(\s*$|^\s*)//g;
                       $Partdata{$part}->{'_Foils'}->{$foil}->{'text'}=$value;
                   } elsif ($key =~ /^foil\.value\.(.*)$/) {
                       my $foil = $1;
                       $Partdata{$part}->{'_Foils'}->{$foil}->{'value'}=$value;
                   } elsif ($key eq 'answercomputed') {
                       $Partdata{$part}->{'answercomputed'} = $value;
                   }
               }
           }
       }
       # Further debugging code
       if (0) {
           &Apache::lonnet::logthis('lonstathelpers::get_problem_data');
           &log_hash_ref(\%Partdata);
       }
       return %Partdata;
   }
   
   sub log_array_ref {
       my ($arrayref,$prefix) = @_;
       return if (ref($arrayref) ne 'ARRAY');
       if (! defined($prefix)) { $prefix = ''; };
       foreach my $v (@$arrayref) {
           if (ref($v) eq 'ARRAY') {
               &log_array_ref($v,$prefix.'  ');
           } elsif (ref($v) eq 'HASH') {
               &log_hash_ref($v,$prefix.'  ');
           } else {
               &Apache::lonnet::logthis($prefix.'"'.$v.'"');
           }
       }
   }
   
   sub log_hash_ref {
       my ($hashref,$prefix) = @_;
       return if (ref($hashref) ne 'HASH');
       if (! defined($prefix)) { $prefix = ''; };
       while (my ($k,$v) = each(%$hashref)) {
           if (ref($v) eq 'ARRAY') {
               &Apache::lonnet::logthis($prefix.'"'.$k.'" = array');
               &log_array_ref($v,$prefix.'  ');
           } elsif (ref($v) eq 'HASH') {
               &Apache::lonnet::logthis($prefix.'"'.$k.'" = hash');
               &log_hash_ref($v,$prefix.'  ');
           } else {
               &Apache::lonnet::logthis($prefix.'"'.$k.'" => "'.$v.'"');
           }
       }
   }
   ####################################################
   ####################################################
   
   =pod
   
   =item &limit_by_time()
   
   =cut
   
   ####################################################
   ####################################################
   sub limit_by_time_form {
       my $Starttime_form = '';
       my $starttime = &Apache::lonhtmlcommon::get_date_from_form
           ('limitby_startdate');
       my $endtime = &Apache::lonhtmlcommon::get_date_from_form
           ('limitby_enddate');
       if (! defined($endtime)) {
           $endtime = time;
       }
       if (! defined($starttime)) {
           $starttime = $endtime - 60*60*24*7;
       }
       my $state;
       if (&limit_by_time()) {
           $state = '';
       } else {
           $state = 'disabled';
       }
       my $startdateform = &Apache::lonhtmlcommon::date_setter
           ('Statistics','limitby_startdate',$starttime,undef,undef,$state);
       my $enddateform = &Apache::lonhtmlcommon::date_setter
           ('Statistics','limitby_enddate',$endtime,undef,undef,$state);
       my $Str;
       $Str .= '<script type="text/javascript" language="JavaScript">';
       $Str .= 'function toggle_limitby_activity(state) {';
       $Str .= '    if (state) {';
       $Str .= '        limitby_startdate_enable();';
       $Str .= '        limitby_enddate_enable();';
       $Str .= '    } else {';
       $Str .= '        limitby_startdate_disable();';
       $Str .= '        limitby_enddate_disable();';
       $Str .= '    }';    
       $Str .= '}';
       $Str .= '</script>';
       $Str .= '<fieldset>';
       my $timecheckbox = '<input type="checkbox" name="limit_by_time" ';
       if (&limit_by_time()) {
           $timecheckbox .= ' checked ';
       } 
       $timecheckbox .= 'OnChange="javascript:toggle_limitby_activity(this.checked);" ';
       $timecheckbox .= ' />';
       $Str .= '<legend><label>'.&mt('[_1] Limit by time',$timecheckbox).'</label></legend>';
       $Str .= &mt('Start Time: [_1]',$startdateform).'<br />';
       $Str .= &mt('&nbsp;End Time: [_1]',$enddateform).'<br />';
       $Str .= '</fieldset>';
       return $Str;
   }
   
   sub limit_by_time {
       if (exists($env{'form.limit_by_time'}) &&
           $env{'form.limit_by_time'} ne '' ) {
           return 1;
       } else {
           return 0;
       }
   }
   
   sub get_time_limits {
       my $starttime = &Apache::lonhtmlcommon::get_date_from_form
           ('limitby_startdate');
       my $endtime = &Apache::lonhtmlcommon::get_date_from_form
           ('limitby_enddate');
       return ($starttime,$endtime);
   }
   
   ####################################################
   ####################################################
   
   =pod
   
   =item &manage_caches
   
   Inputs: $r, apache request object
   
   Returns: An array of scalars containing html for buttons.
   
   =cut
   
   ####################################################
   ####################################################
   sub manage_caches {
       my ($r,$formname,$inputname,$update_message) = @_;
       &Apache::loncoursedata::clear_internal_caches();
       my $sectionkey = 
           join(',',
                map {
                        &escape($_);
                    } sort(&Apache::lonstatistics::get_selected_sections())
                );
       my $statuskey = $Apache::lonstatistics::enrollment_status;
       if (exists($env{'form.ClearCache'}) || 
           exists($env{'form.updatecaches'}) || 
           (exists($env{'form.firstrun'}) && $env{'form.firstrun'} ne 'no') ||
           (exists($env{'form.prevsection'}) &&
               $env{'form.prevsection'} ne $sectionkey) ||
           (exists($env{'form.prevenrollstatus'}) &&
               $env{'form.prevenrollstatus'} ne $statuskey)
           ) {
           if (defined($update_message)) {
               $r->print($update_message);
           }
           if (0) {
               &Apache::lonnet::logthis('Updating mysql student data caches');
           }
           &gather_full_student_data($r,$formname,$inputname);
       }
       #
       my @Buttons = 
           ('<input type="submit" name="ClearCache" '.
                'value="'.&mt('Clear Caches').'" />',
            '<input type="submit" name="updatecaches" '.
                'value="'.&mt('Update Caches').'" />'.
            &Apache::loncommon::help_open_topic('Statistics_Cache'),
            '<input type="hidden" name="prevsection" value="'.$sectionkey.'" />',
            '<input type="hidden" name="prevenrollstatus" value="'.$statuskey.'" />'
            );
       #
       if (! exists($env{'form.firstrun'})) {
           $r->print('<input type="hidden" name="firstrun" value="yes" />');
       } else {
           $r->print('<input type="hidden" name="firstrun" value="no" />');
       }
       #
       return @Buttons;
   }
   
   sub gather_full_student_data {
       my ($r,$formname,$inputname) = @_;
       my $status_type;
       if (defined($formname)) {
           $status_type = 'inline';
       } else {
           $status_type = 'popup';
       }
       my $c = $r->connection();
       #
       &Apache::loncoursedata::clear_internal_caches();
       #
       my @Students = @Apache::lonstatistics::Students;
       #
       # Open the progress window
       my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
           ($r,&mt('Student Data Compilation Status'),
            &mt('Student Data Compilation Progress'), scalar(@Students),
            $status_type,undef,$formname,$inputname);
       #
       while (my $student = shift @Students) {
           return if ($c->aborted());
           my $status = &Apache::loncoursedata::ensure_current_full_data
               ($student->{'username'},$student->{'domain'},
                $env{'request.course.id'});
           &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
                                                    &mt('last student'));
       }
       &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
       $r->rflush();
       return;
   }
   
   ####################################################
   ####################################################
   
   =pod
   
   =item &submission_report_form
   
   Input: The originating reportSelected value for the current stats page.
   
   Output: Scalar containing HTML with needed form elements and a link to 
   the student submission reports page.
   
   =cut
   
   ####################################################
   ####################################################
   sub submission_report_form {
       my ($original_report) = @_;
       # Note: In the link below we change the reportSelected value.  If
       # the user hits the 'back' button on the browser after getting their
       # student submissions report, this value may still be around.  So we
       # output a script block to set it properly.  If the $original_report
       # value is unset, you are just asking for trouble.
       if (! defined($original_report)) {
           &Apache::lonnet::logthis
               ('someone called lonstathelpers::submission_report_form without '.
                ' enough input.');
       }
       my $html = $/.
           '<script type="text/javascript" language="JavaScript">'.
           "document.Statistics.reportSelected.value='$original_report';".
           '</script>'.
           '<input type="hidden" name="correctans" value="true" />'.
           '<input type="hidden" name="prob_status" value="true" />'.
           '<input type="hidden" name="all_sub" value="true" />';
       my $output_selector = $/.'<select name="output">'.$/;
       foreach ('HTML','Excel','CSV') {
           $output_selector .= '    <option value="'.lc($_).'"';
           if ($env{'form.output'} eq lc($_)) {
               $output_selector .= ' selected ';
           }
           $output_selector .='>'.&mt($_).'</option>'.$/;
       } 
       $output_selector .= '</select>'.$/;
       my $link = '<a href="javascript:'.
          q{document.Statistics.reportSelected.value='student_submission_reports';}.
          'document.Statistics.submit();">';
       $html.= &mt('View data as [_1] [_2]go[_3]',$output_selector,
                   $link,'</a>').$/;
       return $html
 }  }
   
 ####################################################  ####################################################

Removed from v.1.2  
changed lines
  Added in v.1.58


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