Diff for /loncom/interface/statistics/lonstathelpers.pm between versions 1.18 and 1.77

version 1.18, 2004/08/03 19:53:12 version 1.77, 2020/08/31 14:36:19
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 61  use Time::Local(); Line 60  use Time::Local();
 use Spreadsheet::WriteExcel();  use Spreadsheet::WriteExcel();
 use GDBM_File;  use GDBM_File;
 use Storable qw(freeze thaw);  use Storable qw(freeze thaw);
   use lib '/home/httpd/lib/perl/';
   use LONCAPA;
    
   
 ####################################################  ####################################################
 ####################################################  ####################################################
Line 69  use Storable qw(freeze thaw); Line 71  use Storable qw(freeze thaw);
   
 =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 83  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 '<div class="LC_Box">'.
         '<base href="'.$base.'" />'.          '<h4 class="LC_hcell">'.&mt('Problem').'</h4>'.
         $rendered_problem.          '<base href="'.$base.'" />'.$rendered_problem.
         '</td></tr></table>';          '</div>';
 }  }
   
 ####################################################  ####################################################
Line 101  sub render_resource { Line 101  sub render_resource {
   
 =pod  =pod
   
 =item &ProblemSelector($AcceptedResponseTypes)  =item &get_resources
   
   =cut
   
   ####################################################
   ####################################################
   sub get_resources {
       my ($navmap,$sequence,$include_tools) = @_;
       my @resources;
       if ($include_tools) {
           @resources = $navmap->retrieveResources($sequence,
                                                   sub { shift->is_gradable(); },
                                                   0,0,0);
       } else {
           @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 115  Skips 'survey' problems. Line 141  Skips 'survey' problems.
   
 ####################################################  ####################################################
 ####################################################  ####################################################
 sub ProblemSelector {  sub problem_selector {
     my ($AcceptedResponseTypes) = @_;      my ($AcceptedResponseTypes,$sequence_addendum,$symbmode,$all,$prefix,
           $byres,$include_tools,$smallbox,$onclick) = @_;
   # all: also make sequences selectable
   # prefix: prefix for all form names
   # byres: radiobutton shown per resource
   # include_tools: external tools included 
   # smallbox: use smaller box
   # onclick: javascript to execute when clicked
     my $Str;      my $Str;
     $Str = "\n<table>\n";      my $jsadd='';
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess('all')) {      if ($onclick) {
         next if ($seq->{'num_assess'}<1);          $jsadd="onclick='$onclick'";
       }
       $Str =  &Apache::loncommon::start_scrollbox(($smallbox?'420px':'620px'),
                                                   ($smallbox?'400px':'600px'),
                                                   ($smallbox?'60px':'300px')).
               &Apache::loncommon::start_data_table();
       my $rb_count =0;
       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,$include_tools)) {
             next if ($res->{'type'} ne 'assessment');              if ($res->src() eq '/res/lib/templates/simpleproblem.problem') {
             foreach my $part (@{$res->{'parts'}}) {                  next if (grep(/^placeholder$/,@{$res->parts}));
                 my $partdata = $res->{'partdata'}->{$part};              }
                 next if ($partdata->{'Survey'});              my $title = $res->compTitle;
                 for (my $i=0;$i<scalar(@{$partdata->{'ResponseTypes'}});$i++){              if (! defined($title) || $title eq '') {
                     my $respid = $partdata->{'ResponseIds'}->[$i];                  ($title) = ($res->src =~ m:/([^/]*)$:);
                     my $resptype = $partdata->{'ResponseTypes'}->[$i];              }
               my $totalresps = 0;
               if ($byres) {
                   foreach my $part (@{$res->parts}) {
                       $totalresps += scalar($res->responseIds($part));
                   }
                   my $value = &HTML::Entities::encode($res->symb(),'<>&"');
                   my $checked;
                   if ($env{'form.problemchoice'} eq $res->symb()) {
                       $checked = ' checked="checked"';
                   }
                   my $rowspan;
                   if ($totalresps > 1) {
                       $rowspan = ' rowspan="'.$totalresps.'"';
                   }
                   $seq_str .= &Apache::loncommon::start_data_table_row().
                               '<td'.$rowspan.' style="vertical-align:middle">'.
                               '<label><input type="radio" name="symb" value="'.$value.'"'.$checked.' />'.
                               $title.'</label>';
                   my $link = $res->link.'?symb='.&escape($res->shown_symb);
                   $seq_str .= ('&nbsp;'x2).
                               '<a target="preview" href="'.$link.'">'.&mt('view').'</a></td>';
               }
               my %partsseen;
               foreach my $part (@{$res->parts}) {
                   my (@response_ids,@response_types);
                   if ($res->is_tool) {
                       @response_ids = ();
                       @response_types = ('tool');
                   } else {
                       @response_ids   = $res->responseIds($part);
                       @response_types = $res->responseType($part);
                   }
                   for (my $i=0;$i<scalar(@response_types);$i++){
                       my $respid = $response_ids[$i];
                       my $resptype = $response_types[$i];
                     if ($resptype =~ m/$AcceptedResponseTypes/) {                      if ($resptype =~ m/$AcceptedResponseTypes/) {
                         my $value = &make_target_id({symb=>$res->{'symb'},                          if ($byres) {
                                                      part=>$part,                              unless (exists($partsseen{$part})) {
                                                      respid=>$respid,                                  my $parttitle = $part;
                                                      resptype=>$resptype});                                  if ($part eq '0') {
                         my $checked = '';                                      $parttitle = '';
                         if ($ENV{'form.problemchoice'} eq $value) {                                  }
                             $checked = 'checked ';                                  if ($parttitle ne '') {
                         }                                      $parttitle = ('&nbsp;'x2).&mt('part').':&nbsp;'.$parttitle;
                         my $title = $res->{'title'};                                  }
                         if (! defined($title) || $title eq '') {                                  if (keys(%partsseen)) {
                             ($title) = ($res->{'src'} =~ m:/([^/]*)$:);                                      $seq_str .= &Apache::loncommon::continue_data_table_row();
                                   }
                                   unless ($partsseen{$part}) {
                                       $seq_str .= '<td rowspan="'.scalar(@response_ids).'" style="vertical-align:middle">'.
                                                   $parttitle.'</td>';
                                       $partsseen{$part} = scalar(@response_ids);
                                   }
                               }
                               $seq_str .= '<td>'.$resptype;
                               if (scalar(@response_ids) > 1) {
                                   $seq_str .= '&nbsp;'.&mt('id').':&nbsp;'.$respid;
                               }
                               $seq_str .= '</td>'. &Apache::loncommon::end_data_table_row()."\n";
                           } else {
                               my $value = &make_target_id({symb=>$res->symb,
                                                            part=>$part,
                                                            respid=>$respid,
                                                            resptype=>$resptype});
                               my $checked = '';
                               if ($env{'form.problemchoice'} eq $value) {
                                   $checked = ' checked="checked"';
                               }
                               $seq_str .= &Apache::loncommon::start_data_table_row().
                                   ($symbmode?
                                    '<td><input type="radio" id="'.$prefix.$rb_count.'" name="'.$prefix.'symb" value="'.&HTML::Entities::encode($res->symb,'<>&"').'" '.$checked.' '.
                                    $jsadd.
                                    ' /></td>'
                                    :qq{<td><input type="radio" id="$rb_count" name="problemchoice" value="$value"$checked /></td>}).
                                   '<td><label for="'.$prefix.$rb_count.'">'.$resptype.'</label></td>'.
                                   '<td><label for="'.$prefix.$rb_count.'">'.$title.'</label>';
                               if (scalar(@response_ids) > 1) {
                                   $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>". &Apache::loncommon::end_data_table_row()."\n";
                               $rb_count++;
                         }                          }
                         $seq_str .= '<tr><td>'.  
   '<input type="radio" name="problemchoice" value="'.$value.'" '.$checked.'/>'.  
   '</td><td>'.            
   $resptype.'</td><td>'.  
   '<a href="'.$res->{'src'}.'">'.$title.'</a> ';  
 #  '<a href="'.$res->{'src'}.'">'.$resptype.' '.$res->{'title'}.'</a> ';  
                         if (scalar(@{$partdata->{'ResponseIds'}}) > 1) {  
                             $seq_str .= &mt('response').' '.$respid;  
                         }  
                         $seq_str .= "</td></tr>\n";  
                     }                      }
                 }                  }
             }              }
         }          }
         if ($seq_str ne '') {          if ($seq_str ne '') {
             $Str .= '<tr><td>&nbsp</td><td colspan="2"><b>'.$seq->{'title'}.'</b></td>'.              if ($byres) {
                 "</tr>\n".$seq_str;                  $Str .= &Apache::loncommon::start_data_table_header_row().
                           '<th colspan="3">'.$seq->compTitle.'</th>'.
                           &Apache::loncommon::end_data_table_header_row().
                           $seq_str;
               } else {
                   $Str .= &Apache::loncommon::start_data_table_header_row().
                       '<th colspan="3">'.
                       ($all?'<input type="radio" id="'.$prefix.'s'.$rb_count.'" name="'.$prefix.'symb" value="'.&HTML::Entities::encode($seq->symb,'<>&').'" '.$jsadd.' />':'').
                       $seq->compTitle.'</th>'.
                       &Apache::loncommon::end_data_table_header_row()."\n".$seq_str;
                   if (defined($sequence_addendum)) {
                       $Str .= &Apache::loncommon::start_data_table_header_row().
                           ('<td>&nbsp;</td>'x2).
                           '<td align="right">'.$sequence_addendum.'</td>'.
                           &Apache::loncommon::end_data_table_header_row()."\n";
                   }
               }
           }
       }
       $Str .= &Apache::loncommon::end_data_table().&Apache::loncommon::end_scrollbox()."\n";
       return $Str;
   }
   
   ####################################################
   ####################################################
   
   =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,$anoncounter)=@_;
       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
       my $checkanonjs = <<"END";
    
   <script type="text/javascript" language="JavaScript">
       function checkanon() {
           return true;
       }
   </script>
   
   END
       if (ref($anoncounter) eq 'HASH') {
           if (keys(%{$anoncounter}) > 0) {
               my $anonwarning = &mt('Your selection includes both problems with and without anonymous submissions.')."\n".&mt('You must select either only anonymous or only named problems.')."\n\n".&mt('If a selection contains both anonymous and named parts,[_1]use the Anonymous/Named buttons to ensure selections will be either all anonymous[_1]or all named.',"\n");
               &js_escape(\$anonwarning);
               $checkanonjs = <<"END";
   
   <script type="text/javascript" language="JavaScript">
       function checkanon() {
           anoncount = 0;
           namedcount = 0;
           for (i=0; i<document.forms.$formname.elements.length; i++) {
               ele = document.forms.$formname.elements[i];
               if (ele.name == '$inputname') {
                   itemid = document.forms.$formname.elements[i].id;
                   if (document.forms.$formname.elements[i].checked) {
                       anonid = 'anonymous_'+itemid;
                       mixid = 'mixed_'+itemid;
                       anonele = document.getElementById(anonid);
                       mixele = document.getElementById(mixid);
                       if (anonele.value > 0) {
                           if (mixele.value == 'none') {
                               anoncount ++;
                           } else {
                               if (mixele.value == '0') {
                                   if (mixele.checked) {
                                       anoncount ++; 
                                   } else {
                                       namedcount ++;
                                   } 
                               } else {
                                   namedcount ++;
                               }
                           }
                       } else {
                           namedcount ++;
                       }
                   }
               }
         }          }
           if (anoncount > 0 && namedcount > 0) {
               alert("$anonwarning");
               return false;
           } 
     }      }
     $Str .= "</table>\n";  </script>
   
   END
           }
       }
       $Str .= $checkanonjs.
           '<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) {
               my $anonpart = 0;
               my $namedpart = 0;
               my @parts = @{$curRes->parts()};
               if (ref($anoncounter) eq 'HASH') {
                   if (keys(%{$anoncounter}) > 0) {
                       my @parts = @{$curRes->parts()};
                       my $symb = $curRes->symb();
                       foreach my $part (@parts) {
                           if ((exists($anoncounter->{$symb."\0".$part})) ||
                               $curRes->is_anonsurvey($part)) {
                               $anonpart ++;
                           } else {
                               $namedpart ++ 
                           }
                       }
                   }
               }
               if (@Accumulator && $Accumulator[-1] ne '') {
                   &{$Accumulator[-1]}($curRes,
                                       exists($selected->{$curRes->symb}),
                                       $anonpart,$namedpart);
               }
           }
       }
       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;      return $Str;
 }  }
   
   sub new_accumulator {
       my ($title,$src,$symb,$seq_id,$inputname) = @_;
       my $target;
       my $item_id=0;
       return 
           sub {
               if (@_) { 
                   my ($res,$checked,$anonpart,$namedpart) = @_;
                   $target.='<tr><td><label>'.
                       '<input type="checkbox" name="'.$inputname.'" ';
                   if ($checked) {
                       $target .= 'checked="checked" ';
                   }
                   my $anon_id = $item_id;
                   $target .= 'id="'.$seq_id.':'.$item_id++.'" ';
                   my $esc_symb = &escape($res->symb);
                   $target.= 
                       'value="'.$esc_symb.'" />'.
                       '&nbsp;'.$res->compTitle.'</label>'.
                       ('&nbsp;'x2).'<a target="preview" '.
                       'href="'.$res->link.'?symb='.
                       &escape($res->shown_symb).'">'.&mt('view').'</a>'.
                       '<input type="hidden" id="anonymous_'.$seq_id.':'.$anon_id.'" name="hidden_'.$seq_id.':'.$anon_id.'" value="'.$anonpart.'" />';
                   my $mixed = '<input type="hidden" id="mixed_'.$seq_id.':'.$anon_id.'" value="none" name="mixed_'.$seq_id.':'.$anon_id.'" />';
                   if ($anonpart) {
                       if ($namedpart) {
                           my $checknamed = '';
                           my $checkedanon = ' checked="checked"';
                           if ($env{'form.mixed_'.$seq_id.':'.$anon_id} eq $esc_symb) {
                               $checknamed = $checkedanon;
                               $checkedanon = '';
                           }
                           $mixed = '&nbsp;('.
       &mt('Both anonymous and named submissions -- display: [_1]Anonymous [_2]Named[_3]',
       '<span class="LC_nobreak"><label>'.
       '<input type="radio" name="mixed_'.$seq_id.':'.$anon_id.
       '" value="0" id="mixed_'.$seq_id.':'.$anon_id.'"'.$checkedanon.' />',
       '</label></span>'.('&nbsp;'x2).' <span class="LC_nobreak">'.
       '<label><input type="radio" name="mixed_'.$seq_id.':'.$anon_id.
       '" value="symb_'.$esc_symb.'" id="named_'.$seq_id.':'.$anon_id.'"'.$checknamed.' />',
       '</label></span>').')';
                       } else {
                           $target .= '&nbsp;'.&mt('(Anonymous Survey)');
                       }
                   }
                   $target.= $mixed.'</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;
   }
   
 ####################################################  ####################################################
 ####################################################  ####################################################
   
Line 188  Used by Apache::lonstathelpers::ProblemS Line 568  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 214  Returns: A hash reference, $target, cont Line 594  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 234  current resource. Line 626  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', 'response', or 'part_survey'    $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 257  sub get_prev_curr_next { Line 649  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('all')) {      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 ($partdata->{'Survey'}) {          foreach my $res (@resources) {
                     if ($granularity eq 'part_survey'){              foreach my $part (@{$res->parts}) {
                         push (@Resource,                  if (($res->is_survey($part) || ($res->is_anonsurvey($part))) && 
                               { symb     => $res->{symb},                      ($granularity eq 'part_survey')) {
                                 part     => $part,                      push (@Resource,
                                 resource => $res,                            { symb     => $res->symb,
                             } );                              part     => $part,
                     }                              resource => $res,
                           } );
    } elsif ($res->is_task($part) && ($granularity eq 'part_task')){
                       push (@Resource,
                             { symb     => $res->symb,
                               part     => $part,
                               resource => $res,
                           } );
                 } elsif ($granularity eq 'part') {                  } elsif ($granularity eq 'part') {
                     push (@Resource,                      push (@Resource,
                           { symb     => $res->{symb},                            { 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 300  sub get_prev_curr_next { Line 701  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' || $granularity eq 'part_survey') {          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 315  sub get_prev_curr_next { Line 716  sub get_prev_curr_next {
         }          }
     }      }
     my $curr_item = $Resource[$curr_idx];      my $curr_item = $Resource[$curr_idx];
     if ($granularity eq 'part' || $granularity eq 'part_survey') {      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 346  sub get_prev_curr_next { Line 747  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);
 }  }
   
   
Line 374  Returns: nothing Line 775  Returns: nothing
 #####################################################  #####################################################
 sub GetStudentAnswers {  sub GetStudentAnswers {
     my ($r,$problem,$Students,$formname,$inputname) = @_;      my ($r,$problem,$Students,$formname,$inputname) = @_;
       my %answers;
     my $status_type;      my $status_type;
     if (defined($formname)) {      if (defined($formname)) {
         $status_type = 'inline';          $status_type = 'inline';
Line 388  sub GetStudentAnswers { Line 790  sub GetStudentAnswers {
     # Read in the cache (if it exists) before we start timing things.      # Read in the cache (if it exists) before we start timing things.
     &Apache::lonstathelpers::ensure_proper_cache($resource->{'symb'});      &Apache::lonstathelpers::ensure_proper_cache($resource->{'symb'});
     # Open progress window      # Open progress window
     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,scalar(@$Students));
         ($r,'Student Answer Compilation Status',  
          'Student Answer Compilation Progress', scalar(@$Students),  
          $status_type,undef,$formname,$inputname);  
     $r->rflush();      $r->rflush();
     foreach my $student (@$Students) {      foreach my $student (@$Students) {
         last if ($c->aborted());          last if ($c->aborted());
         my $sname = $student->{'username'};          my $sname = $student->{'username'};
         my $sdom = $student->{'domain'};          my $sdom = $student->{'domain'};
         my $answer = &Apache::lonstathelpers::analyze_problem_as_student          my $answer = &Apache::lonstathelpers::get_student_answer
             ($resource,$sname,$sdom,$partid,$respid);              ($resource,$sname,$sdom,$partid,$respid);
         &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,          &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
                                                  &mt('last student'));                                                       'last student');
           $answers{$answer}++;
         $student->{'answer'} = $answer;          $student->{'answer'} = $answer;
     }      }
     &Apache::lonstathelpers::write_answer_cache();      &Apache::lonstathelpers::write_analysis_cache();
     return if ($c->aborted());      return if ($c->aborted());
     $r->rflush();      $r->rflush();
     # close progress window      # close progress window
     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);      &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
     return;      return \%answers;
 }  }
   
 #####################################################  #####################################################
Line 418  sub GetStudentAnswers { Line 818  sub GetStudentAnswers {
   
 =item analyze_problem_as_student  =item analyze_problem_as_student
   
 Analyzes a homework problem for a student and returns the correct answer  Analyzes a homework problem for a student
 for the student.  Attempts to put together an answer for problem types   
 that do not natively support it.  
   
 Inputs: $resource: a resource object  Inputs: $resource: a resource object
         $sname, $sdom, $partid, $respid          $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  Returns: $answer
   
 If $partid and $respid are specified, $answer is simply a scalar containing  If $partid and $respid are specified, $answer is simply a scalar containing
Line 437  keys $partid.'.'.$respid.'.answer'. Line 885  keys $partid.'.'.$respid.'.answer'.
   
 #####################################################  #####################################################
 #####################################################  #####################################################
 sub analyze_problem_as_student {  sub get_student_answer {
     my ($resource,$sname,$sdom,$partid,$respid) = @_;      my ($resource,$sname,$sdom,$partid,$respid) = @_;
     my $returnvalue;  
     my $url = $resource->{'src'};  
     my $symb = $resource->{'symb'};  
     my $answer = &get_from_answer_cache($sname,$sdom,$symb,$partid,$respid);  
     if (defined($answer)) {  
         return($answer);  
     }  
     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,$Answ)=split(/_HASH_REF__/,$Answ,2);  
     my %Answer=&Apache::lonnet::str2hash($Answ);  
     #      #
     undef($answer);      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'}}) {      foreach my $partid (@{$resource->{'parts'}}) {
         my $partdata = $resource->{'partdata'}->{$partid};          my $partdata = $resource->{'partdata'}->{$partid};
         foreach my $respid (@{$partdata->{'ResponseIds'}}) {          foreach my $respid (@{$partdata->{'ResponseIds'}}) {
             my $prefix = $partid.'.'.$respid;              my $prefix = $partid.'.'.$respid;
             my $key = $prefix.'.answer';              my $key = $prefix.'.answer';
             $answer->{$partid}->{$respid} = &get_answer($prefix,$key,%Answer);              $answer->{$partid}->{$respid} = 
                   &get_answer($prefix,$key,%$analysis);
         }          }
     }      }
     &store_answer($sname,$sdom,$symb,undef,undef,$answer);      my $returnvalue;
     if (! defined($partid)) {      if (! defined($partid)) {
         $returnvalue = $answer;          $returnvalue = $answer;
     } elsif (! defined($respid)) {      } elsif (! defined($respid)) {
Line 479  sub get_answer { Line 926  sub get_answer {
     my ($prefix,$key,%Answer) = @_;      my ($prefix,$key,%Answer) = @_;
     my $returnvalue;      my $returnvalue;
     if (exists($Answer{$key})) {      if (exists($Answer{$key})) {
         my $student_answer = $Answer{$key}->[0];   if (ref($Answer{$key}) eq 'HASH') {
         if (! defined($student_answer)) {      my $which = 'INTERNAL';
             $student_answer = $Answer{$key}->[1];      if (!exists($Answer{$key}{$which})) {
         }   $which = (sort(keys(%{ $Answer{$key} })))[0];
         $returnvalue = $student_answer;      }
       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 {      } else {
         if (exists($Answer{$prefix.'.shown'})) {          if (exists($Answer{$prefix.'.shown'})) {
             # The response has foils              # The response has foils
Line 509  sub get_answer { Line 961  sub get_answer {
     return $returnvalue;      return $returnvalue;
 }  }
   
   
 #####################################################  #####################################################
 #####################################################  #####################################################
   
Line 519  sub get_answer { Line 970  sub get_answer {
   
 =over 4  =over 4
   
 =item &load_answer_cache($symb)  =item &load_analysis_cache($symb)
   
 Loads the cache for the given symb into memory from disk.    Loads the cache for the given symb into memory from disk.  
 Requires the cache filename be set.    Requires the cache filename be set.  
Line 534  Only should be called by &ensure_proper_ Line 985  Only should be called by &ensure_proper_
     my $current_symb = undef;      my $current_symb = undef;
     my %cache;      my %cache;
   
 sub load_answer_cache {  sub load_analysis_cache {
     my ($symb) = @_;      my ($symb) = @_;
     return if (! defined($cache_filename));      return if (! defined($cache_filename));
     if (! defined($current_symb) || $current_symb ne $symb) {      if (! defined($current_symb) || $current_symb ne $symb) {
Line 542  sub load_answer_cache { Line 993  sub load_answer_cache {
         my $storedstring;          my $storedstring;
         my %cache_db;          my %cache_db;
         if (tie(%cache_db,'GDBM_File',$cache_filename,&GDBM_READER(),0640)) {          if (tie(%cache_db,'GDBM_File',$cache_filename,&GDBM_READER(),0640)) {
             $storedstring = $cache_db{&Apache::lonnet::escape($symb)};              $storedstring = $cache_db{&escape($symb)};
             untie(%cache_db);              untie(%cache_db);
         }          }
         if (defined($storedstring)) {          if (defined($storedstring)) {
Line 557  sub load_answer_cache { Line 1008  sub load_answer_cache {
   
 =pod  =pod
   
 =item &get_from_answer_cache($sname,$sdom,$symb,$partid,$respid)  =item &get_from_analysis_cache($sname,$sdom,$symb,$partid,$respid)
   
 Returns the appropriate data from the cache, or undef if no data exists.  Returns the appropriate data from the cache, or undef if no data exists.
 If $respid is undefined, a hash ref containing the answers for the given   
 $partid is returned.  If $partid is undefined, a hash ref containing answers  
 for all of the parts is returned.  
   
 =cut  =cut
   
 #####################################################  #####################################################
 #####################################################  #####################################################
 sub get_from_answer_cache {  sub get_from_analysis_cache {
     my ($sname,$sdom,$symb,$partid,$respid) = @_;      my ($sname,$sdom,$symb) = @_;
     &ensure_proper_cache($symb);      &ensure_proper_cache($symb);
     my $returnvalue;      my $returnvalue;
     if (exists($cache{$sname.':'.$sdom}) &&      if (exists($cache{$sname.':'.$sdom})) {
         ref($cache{$sname.':'.$sdom}) eq 'HASH') {          $returnvalue = $cache{$sname.':'.$sdom};
         if (defined($partid) &&  
             exists($cache{$sname.':'.$sdom}->{$partid})) {  
             if (defined($respid) &&  
                 exists($cache{$sname.':'.$sdom}->{$partid}->{$respid})) {  
                 $returnvalue = $cache{$sname.':'.$sdom}->{$partid}->{$respid};  
             } else {  
                 $returnvalue = $cache{$sname.':'.$sdom}->{$partid};  
             }  
         } else {  
             $returnvalue = $cache{$sname.':'.$sdom};  
         }  
     } else {      } else {
         $returnvalue = undef;          $returnvalue = undef;
     }      }
Line 596  sub get_from_answer_cache { Line 1033  sub get_from_answer_cache {
   
 =pod  =pod
   
 =item &write_answer_cache($symb)  =item &write_analysis_cache($symb)
   
 Writes the in memory cache to disk so that it can be read in with  Writes the in memory cache to disk so that it can be read in with
 &load_answer_cache($symb).  &load_analysis_cache($symb).
   
 =cut  =cut
   
 #####################################################  #####################################################
 #####################################################  #####################################################
 sub write_answer_cache {  sub write_analysis_cache {
     return if (! defined($current_symb) || ! defined($cache_filename));      return if (! defined($current_symb) || ! defined($cache_filename));
     my %cache_db;      my %cache_db;
     my $key = &Apache::lonnet::escape($current_symb);      my $key = &escape($current_symb);
     if (tie(%cache_db,'GDBM_File',$cache_filename,&GDBM_WRCREAT(),0640)) {      if (tie(%cache_db,'GDBM_File',$cache_filename,&GDBM_WRCREAT(),0640)) {
         my $storestring = freeze(\%cache);          my $storestring = freeze(\%cache);
         $cache_db{$key}=$storestring;          $cache_db{$key}=$storestring;
Line 629  sub write_answer_cache { Line 1066  sub write_answer_cache {
 =item &ensure_proper_cache($symb)  =item &ensure_proper_cache($symb)
   
 Called to make sure we have the proper cache set up.  This is called  Called to make sure we have the proper cache set up.  This is called
 prior to every answer lookup.  prior to every analysis lookup.
   
 =cut  =cut
   
Line 637  prior to every answer lookup. Line 1074  prior to every answer lookup.
 #####################################################  #####################################################
 sub ensure_proper_cache {  sub ensure_proper_cache {
     my ($symb) = @_;      my ($symb) = @_;
     my $cid = $ENV{'request.course.id'};      my $cid = $env{'request.course.id'};
     my $new_filename =  '/home/httpd/perl/tmp/'.      my $new_filename = LONCAPA::tempdir() .
         'problemanalysis_'.$cid.'_answer_cache.db';          'problemanalysis_'.$cid.'_analysis_cache.db';
     if (! defined($cache_filename) ||      if (! defined($cache_filename) ||
         $cache_filename ne $new_filename ||          $cache_filename ne $new_filename ||
         ! defined($current_symb)   ||          ! defined($current_symb)   ||
         $current_symb ne $symb) {          $current_symb ne $symb) {
         $cache_filename = $new_filename;          $cache_filename = $new_filename;
         # Notice: $current_symb is not set to $symb until after the cache is          # Notice: $current_symb is not set to $symb until after the cache is
         # loaded.  This is what tells &load_answer_cache to load in a new          # loaded.  This is what tells &load_analysis_cache to load in a new
         # symb cache.          # symb cache.
         &load_answer_cache($symb);          &load_analysis_cache($symb);
         $current_symb = $symb;          $current_symb = $symb;
     }      }
 }  }
Line 658  sub ensure_proper_cache { Line 1095  sub ensure_proper_cache {
   
 =pod  =pod
   
 =item &store_answer($sname,$sdom,$symb,$partid,$respid,$dataset)  =item &store_analysis($sname,$sdom,$symb,$partid,$respid,$dataset)
   
 Stores the answer data in the in memory cache.  Stores the analysis data in the in memory cache.
   
 =cut  =cut
   
 #####################################################  #####################################################
 #####################################################  #####################################################
 sub store_answer {  sub store_analysis {
     my ($sname,$sdom,$symb,$partid,$respid,$dataset) = @_;      my ($sname,$sdom,$symb,$dataset) = @_;
     return if ($symb ne $current_symb);      return if ($symb ne $current_symb);
     if (defined($partid)) {      $cache{$sname.':'.$sdom}=$dataset;
         if (defined($respid)) {  
             $cache{$sname.':'.$sdom}->{$partid}->{$respid} = $dataset;  
         } else {  
             $cache{$sname.':'.$sdom}->{$partid} = $dataset;  
         }  
     } else {  
         $cache{$sname.':'.$sdom}=$dataset;  
     }  
     return;      return;
 }  }
   
Line 910  sub get_problem_data { Line 1339  sub get_problem_data {
                 }                  }
             }              }
             # End of logging code              # End of logging code
             next if ($key !~ /^$part/);              next if ($key !~ /^\Q$part\E/);
             $key =~ s/^$part\.//;              $key =~ s/^\Q$part\E\.//;
             if (ref($value) eq 'ARRAY') {              if (ref($value) eq 'ARRAY') {
                 if ($key eq 'options') {                  if ($key eq 'options') {
                     $Partdata{$part}->{'_Options'}=$value;                      $Partdata{$part}->{'_Options'}=$value;
                 } elsif ($key eq 'concepts') {                  } elsif ($key eq 'concepts') {
                     $Partdata{$part}->{'_Concepts'}=$value;                      $Partdata{$part}->{'_Concepts'}=$value;
                   } elsif ($key eq 'items') {
                       $Partdata{$part}->{'_Items'}=$value;
                 } elsif ($key =~ /^concept\.(.*)$/) {                  } elsif ($key =~ /^concept\.(.*)$/) {
                     my $concept = $1;                      my $concept = $1;
                     foreach my $foil (@$value) {                      foreach my $foil (@$value) {
                         $Partdata{$part}->{'_Foils'}->{$foil}->{'_Concept'}=                          $Partdata{$part}->{'_Foils'}->{$foil}->{'_Concept'}=
                                                                       $concept;                                                                        $concept;
                     }                      }
                 } elsif ($key =~ /^(incorrect|answer|ans_low|ans_high)$/) {                  } elsif ($key =~ /^(unit|incorrect|answer|ans_low|ans_high|str_type)$/) {
                     $Partdata{$part}->{$key}=$value;                      $Partdata{$part}->{$key}=$value;
                 }                  }
             } else {              } else {
Line 935  sub get_problem_data { Line 1366  sub get_problem_data {
                 } elsif ($key =~ /^foil\.value\.(.*)$/) {                  } elsif ($key =~ /^foil\.value\.(.*)$/) {
                     my $foil = $1;                      my $foil = $1;
                     $Partdata{$part}->{'_Foils'}->{$foil}->{'value'}=$value;                      $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;      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.'"');
           }
       }
   }
 ####################################################  ####################################################
 ####################################################  ####################################################
   
Line 976  sub limit_by_time_form { Line 1445  sub limit_by_time_form {
     my $enddateform = &Apache::lonhtmlcommon::date_setter      my $enddateform = &Apache::lonhtmlcommon::date_setter
         ('Statistics','limitby_enddate',$endtime,undef,undef,$state);          ('Statistics','limitby_enddate',$endtime,undef,undef,$state);
     my $Str;      my $Str;
     $Str .= '<script language="Javascript" >';      $Str .= '<script type="text/javascript" language="JavaScript">';
     $Str .= 'function toggle_limitby_activity(state) {';      $Str .= 'function toggle_limitby_activity(state) {';
     $Str .= '    if (state) {';      $Str .= '    if (state) {';
     $Str .= '        limitby_startdate_enable();';      $Str .= '        limitby_startdate_enable();';
Line 990  sub limit_by_time_form { Line 1459  sub limit_by_time_form {
     $Str .= '<fieldset>';      $Str .= '<fieldset>';
     my $timecheckbox = '<input type="checkbox" name="limit_by_time" ';      my $timecheckbox = '<input type="checkbox" name="limit_by_time" ';
     if (&limit_by_time()) {      if (&limit_by_time()) {
         $timecheckbox .= ' checked ';          $timecheckbox .= 'checked="checked" ';
     }       } 
     $timecheckbox .= 'OnChange="javascript:toggle_limitby_activity(this.checked);" ';      $timecheckbox .= 'onchange="javascript:toggle_limitby_activity(this.checked);" ';
     $timecheckbox .= ' />';      $timecheckbox .= ' />';
     $Str .= '<legend>'.&mt('[_1] Limit by time',$timecheckbox).'</legend>';      $Str .= '<legend><label>'.&mt('[_1] Limit by time',$timecheckbox).'</label></legend>';
     $Str .= &mt('Start Time: [_1]',$startdateform).'<br />';      $Str .= &mt('Start Time: [_1]',$startdateform).'<br />';
     $Str .= &mt('&nbsp;End Time: [_1]',$enddateform).'<br />';      $Str .= &mt('&nbsp;End Time: [_1]',$enddateform).'<br />';
     $Str .= '</fieldset>';      $Str .= '</fieldset>';
Line 1002  sub limit_by_time_form { Line 1471  sub limit_by_time_form {
 }  }
   
 sub limit_by_time {  sub limit_by_time {
     if (exists($ENV{'form.limit_by_time'}) &&      if (exists($env{'form.limit_by_time'}) &&
         $ENV{'form.limit_by_time'} ne '' ) {          $env{'form.limit_by_time'} ne '' ) {
         return 1;          return 1;
     } else {      } else {
         return 0;          return 0;
Line 1018  sub get_time_limits { Line 1487  sub get_time_limits {
     return ($starttime,$endtime);      return ($starttime,$endtime);
 }  }
   
   
   
 ####################################################  
 ####################################################  
   
 =pod  
   
 =item sections_description   
   
 Inputs: @Sections, an array of sections  
   
 Returns: A text description of the sections selected.  
   
 =cut  
   
 ####################################################  
 ####################################################  
 sub sections_description {  
     my @Sections = @_;  
     my $sectionstring = '';  
     if (scalar(@Sections) > 1) {  
         if (scalar(@Sections) > 2) {  
             my $last = pop(@Sections);  
             $sectionstring = "Sections ".join(', ',@Sections).', and '.$last;  
         } else {  
             $sectionstring = "Sections ".join(' and ',@Sections);  
         }  
     } else {  
         if ($Sections[0] eq 'all') {  
             $sectionstring = "All sections";  
         } else {  
             $sectionstring = "Section ".$Sections[0];  
         }  
     }  
     return $sectionstring;  
 }  
   
 ####################################################  ####################################################
 ####################################################  ####################################################
   
Line 1071  Returns: An array of scalars containing Line 1503  Returns: An array of scalars containing
 ####################################################  ####################################################
 ####################################################  ####################################################
 sub manage_caches {  sub manage_caches {
     my ($r,$formname,$inputname) = @_;      my ($r,$formname,$inputname,$update_message) = @_;
     &Apache::loncoursedata::clear_internal_caches();      &Apache::loncoursedata::clear_internal_caches();
     my $sectionkey =       my $sectionkey = 
         join(',',          join(',',
              map {               map {
                      &Apache::lonnet::escape($_);                       &escape($_);
                  } sort(@Apache::lonstatistics::SelectedSections)                   } sort(&Apache::lonstatistics::get_selected_sections())
              );               );
     my $statuskey = $Apache::lonstatistics::enrollment_status;      my $statuskey = $Apache::lonstatistics::enrollment_status;
     if (exists($ENV{'form.ClearCache'}) ||       if (exists($env{'form.ClearCache'}) || 
         exists($ENV{'form.updatecaches'}) ||           exists($env{'form.updatecaches'}) || 
         (exists($ENV{'form.firstrun'}) && $ENV{'form.firstrun'} ne 'no') ||          (exists($env{'form.firstrun'}) && $env{'form.firstrun'} ne 'no') ||
         (exists($ENV{'form.prevsection'}) &&          (exists($env{'form.prevsection'}) &&
             $ENV{'form.prevsection'} ne $sectionkey) ||              $env{'form.prevsection'} ne $sectionkey) ||
         (exists($ENV{'form.prevenrollstatus'}) &&          (exists($env{'form.prevenrollstatus'}) &&
             $ENV{'form.prevenrollstatus'} ne $statuskey)              $env{'form.prevenrollstatus'} ne $statuskey)
         ) {          ) {
         &Apache::lonstatistics::Gather_Full_Student_Data($r,$formname,          if (defined($update_message)) {
                                                          $inputname);              $r->print($update_message);
           }
           if (0) {
               &Apache::lonnet::logthis('Updating mysql student data caches');
           }
           &gather_full_student_data($r,$formname,$inputname);
     }      }
     #      #
     my @Buttons =       my @Buttons = 
Line 1102  sub manage_caches { Line 1539  sub manage_caches {
          '<input type="hidden" name="prevenrollstatus" value="'.$statuskey.'" />'           '<input type="hidden" name="prevenrollstatus" value="'.$statuskey.'" />'
          );           );
     #      #
     if (! exists($ENV{'form.firstrun'})) {      if (! exists($env{'form.firstrun'})) {
         $r->print('<input type="hidden" name="firstrun" value="yes" />');          $r->print('<input type="hidden" name="firstrun" value="yes" />');
     } else {      } else {
         $r->print('<input type="hidden" name="firstrun" value="no" />');          $r->print('<input type="hidden" name="firstrun" value="no" />');
Line 1111  sub manage_caches { Line 1548  sub manage_caches {
     return @Buttons;      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,scalar(@Students));
       #
       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.18  
changed lines
  Added in v.1.77


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