--- loncom/interface/statistics/lonstathelpers.pm	2004/08/12 12:54:12	1.19
+++ loncom/interface/statistics/lonstathelpers.pm	2004/10/04 19:11:38	1.28
@@ -1,6 +1,6 @@
 # The LearningOnline Network with CAPA
 #
-# $Id: lonstathelpers.pm,v 1.19 2004/08/12 12:54:12 matthew Exp $
+# $Id: lonstathelpers.pm,v 1.28 2004/10/04 19:11:38 matthew Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -40,7 +40,6 @@ routines that are needed across multiple
 
 =head1 OVERVIEW
 
-
 =over 4
 
 =cut
@@ -119,6 +118,7 @@ sub ProblemSelector {
     my ($AcceptedResponseTypes) = @_;
     my $Str;
     $Str = "\n<table>\n";
+    my $rb_count =0;
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess('all')) {
         next if ($seq->{'num_assess'}<1);
         my $seq_str = '';
@@ -142,16 +142,17 @@ sub ProblemSelector {
                         if (! defined($title) || $title eq '') {
                             ($title) = ($res->{'src'} =~ m:/([^/]*)$:);
                         }
-                        $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> ';
+                        $seq_str .= '<tr>'.
+                            qq{<td><input type="radio" id="$rb_count" name="problemchoice" value="$value" $checked /></td>}.
+                            '<td><label for="'.$rb_count.'">'.$resptype.'</label></td>'.
+                            '<td><label for="'.$rb_count.'">'.$title.'</label>';
                         if (scalar(@{$partdata->{'ResponseIds'}}) > 1) {
                             $seq_str .= &mt('response').' '.$respid;
                         }
+                        $seq_str .= ('&nbsp;'x2).
+                            qq{<a target="preview" href="$res->{'src'}">view</a>};
                         $seq_str .= "</td></tr>\n";
+                        $rb_count++;
                     }
                 }
             }
@@ -170,6 +171,177 @@ sub ProblemSelector {
 
 =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 .= 
+                '<h1>'.&mt('Error: cannot process course structure').'</h1>';
+            return $Str;
+        }
+    }
+    my $selected = {map { ($_,1) } (&get_selected_symbs($inputname))};
+    # Header
+    $Str .= <<"END";
+<script language="JavaScript" type="text/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 get_title {
+    my ($title,$src) = @_;
+    if ($title eq '') {
+        ($title) = ($src =~ m|/([^/]+)$|);
+    } else {
+        $title =~ s/\&colon;/:/g;
+    }
+    return $title;
+}
+
+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 ';
+                }
+                $target .= 'id="'.$seq_id.':'.$item_id++.'" ';
+                $target.= 
+                    'value="'.&Apache::lonnet::escape($res->symb).'" />'.
+                    '&nbsp;'.$res->compTitle.'</label>'.
+                    ('&nbsp;'x2).'<a target="preview" '.
+                    'href="'.$res->src.'">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;
+    if (exists($ENV{$field})) {
+        if (! ref($ENV{$field})) {
+            @Symbs = (&Apache::lonnet::unescape($ENV{$field}));
+        } else {
+            @Symbs = (map {&Apache::lonnet::unescape($_);} @{$ENV{$field}});
+        }
+    }
+    return @Symbs;
+}
+
+####################################################
+####################################################
+
+=pod
+
 =item &make_target_id($target)
 
 Inputs: Hash ref with the following entries:
@@ -213,11 +385,23 @@ Returns: A hash reference, $target, cont
 ####################################################
 sub get_target_from_id {
     my ($id) = @_;
-    my ($symb,$part,$respid,$resptype) = split(':',$id);
-    return ({ symb    =>&Apache::lonnet::unescape($symb),
-             part     =>&Apache::lonnet::unescape($part),
-             respid   =>&Apache::lonnet::unescape($respid),
-             resptype =>&Apache::lonnet::unescape($resptype)});
+    if (! ref($id)) {
+        my ($symb,$part,$respid,$resptype) = split(':',$id);
+        return ({ symb     => &Apache::lonnet::unescape($symb),
+                  part     => &Apache::lonnet::unescape($part),
+                  respid   => &Apache::lonnet::unescape($respid),
+                  resptype => &Apache::lonnet::unescape($resptype)});
+    } elsif (ref($id) eq 'ARRAY') {
+        my @Return;
+        foreach my $selected (@$id) {
+            my ($symb,$part,$respid,$resptype) = split(':',$selected);
+            push(@Return,{ symb     => &Apache::lonnet::unescape($symb),
+                           part     => &Apache::lonnet::unescape($part),
+                           respid   => &Apache::lonnet::unescape($respid),
+                           resptype => &Apache::lonnet::unescape($resptype)});
+        }
+        return \@Return;
+    }
 }
 
 ####################################################
@@ -261,14 +445,12 @@ sub get_prev_curr_next {
             next if ($res->{'type'} ne 'assessment');
             foreach my $part (@{$res->{'parts'}}) {
                 my $partdata = $res->{'partdata'}->{$part};
-                if ($partdata->{'Survey'}) {
-                    if ($granularity eq 'part_survey'){
-                        push (@Resource,
-                              { symb     => $res->{symb},
-                                part     => $part,
-                                resource => $res,
-                            } );
-                    }
+                if ($partdata->{'Survey'} && ($granularity eq 'part_survey')){
+                    push (@Resource,
+                          { symb     => $res->{symb},
+                            part     => $part,
+                            resource => $res,
+                        } );
                 } elsif ($granularity eq 'part') {
                     push (@Resource,
                           { symb     => $res->{symb},
@@ -438,6 +620,16 @@ keys $partid.'.'.$respid.'.answer'.
 #####################################################
 sub analyze_problem_as_student {
     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 $returnvalue;
     my $url = $resource->{'src'};
     my $symb = $resource->{'symb'};
@@ -916,13 +1108,15 @@ sub get_problem_data {
                     $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 =~ /^(incorrect|answer|ans_low|ans_high)$/) {
+                } elsif ($key =~ /^(incorrect|answer|ans_low|ans_high|str_type)$/) {
                     $Partdata{$part}->{$key}=$value;
                 }
             } else {
@@ -934,13 +1128,51 @@ sub get_problem_data {
                 } 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.'"');
+        }
+    }
+}
 ####################################################
 ####################################################
 
@@ -1070,7 +1302,7 @@ Returns: An array of scalars containing
 ####################################################
 ####################################################
 sub manage_caches {
-    my ($r,$formname,$inputname) = @_;
+    my ($r,$formname,$inputname,$update_message) = @_;
     &Apache::loncoursedata::clear_internal_caches();
     my $sectionkey = 
         join(',',
@@ -1087,8 +1319,12 @@ sub manage_caches {
         (exists($ENV{'form.prevenrollstatus'}) &&
             $ENV{'form.prevenrollstatus'} ne $statuskey)
         ) {
+        if (defined($update_message)) {
+            $r->print($update_message);
+        }
         &Apache::lonstatistics::Gather_Full_Student_Data($r,$formname,
                                                          $inputname);
+            
     }
     #
     my @Buttons =