Diff for /loncom/homework/grades.pm between versions 1.596.2.12.2.57 and 1.801

version 1.596.2.12.2.57, 2021/01/25 14:25:55 version 1.801, 2024/12/09 22:22:57
Line 44  use Apache::Constants qw(:common :http); Line 44  use Apache::Constants qw(:common :http);
 use Apache::lonlocal;  use Apache::lonlocal;
 use Apache::lonenc;  use Apache::lonenc;
 use Apache::lonstathelpers;  use Apache::lonstathelpers;
   use Apache::lonquickgrades;
 use Apache::bridgetask();  use Apache::bridgetask();
 use Apache::lontexconvert();  use Apache::lontexconvert();
   use Apache::loncourserespicker;
   use String::Similarity;
 use HTML::Parser();  use HTML::Parser();
 use File::MMagic;  use File::MMagic;
 use String::Similarity;  
 use LONCAPA;  use LONCAPA;
   use LONCAPA::ltiutils();
   
 use POSIX qw(floor);  use POSIX qw(floor);
   
Line 64  my $ssi_retries = 5; Line 67  my $ssi_retries = 5;
 my $ssi_error;  my $ssi_error;
 my $ssi_error_resource;  my $ssi_error_resource;
 my $ssi_error_message;  my $ssi_error_message;
   my $registered_cleanup;
   
 sub ssi_with_retries {  sub ssi_with_retries {
     my ($resource, $retries, %form) = @_;      my ($resource, $retries, %form) = @_;
Line 118  sub getpartlist { Line 121  sub getpartlist {
     my $res      = $navmap->getBySymb($symb);      my $res      = $navmap->getBySymb($symb);
     my $partlist = $res->parts();      my $partlist = $res->parts();
     my $url      = $res->src();      my $url      = $res->src();
     my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys'));      my $toolsymb;
       if ($url =~ /ext\.tool$/) {
           $toolsymb = $symb;
       }
       my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys',$toolsymb));
   
     my @stores;      my @stores;
     foreach my $part (@{ $partlist }) {      foreach my $part (@{ $partlist }) {
Line 142  sub nameUserString { Line 149  sub nameUserString {
 }  }
   
 #--- Get the partlist and the response type for a given problem. ---  #--- Get the partlist and the response type for a given problem. ---
 #--- Indicate if a response type is coded handgraded or not. ---  
 #--- Count responseIDs, essayresponse items, and dropbox items ---  #--- Count responseIDs, essayresponse items, and dropbox items ---
 #--- Sets response_error pointer to "1" if navmaps object broken ---  #--- Sets response_error pointer to "1" if navmaps object broken ---
 sub response_type {  sub response_type {
Line 299  sub showResourceInfo { Line 305  sub showResourceInfo {
         } else {          } else {
             return '<br clear="all" />';              return '<br clear="all" />';
         }          }
     }        }
     return $result;      return $result;
 }  }
   
Line 586  sub cleanRecord { Line 592  sub cleanRecord {
     return $result;      return $result;
  }   }
     } elsif ( $response =~ m/(?:numerical|formula|custom)/) {      } elsif ( $response =~ m/(?:numerical|formula|custom)/) {
         # Respect multiple input fields, see Bug #5409           # Respect multiple input fields, see Bug #5409
  $answer =    $answer = 
     &Apache::loncommon::format_previous_attempt_value('submission',      &Apache::loncommon::format_previous_attempt_value('submission',
       $answer);        $answer);
Line 632  COMMONJSFUNCTIONS Line 638  COMMONJSFUNCTIONS
 #--- Dumps the class list with usernames,list of sections,  #--- Dumps the class list with usernames,list of sections,
 #--- section, ids and fullnames for each user.  #--- section, ids and fullnames for each user.
 sub getclasslist {  sub getclasslist {
     my ($getsec,$filterbyaccstatus,$getgroup,$symb,$submitonly,$filterbysubmstatus) = @_;      my ($getsec,$filterbyaccstatus,$getgroup,$symb,$submitonly,$filterbysubmstatus,$filterbypbid,$possibles) = @_;
     my @getsec;      my @getsec;
     my @getgroup;      my @getgroup;
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));      my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
Line 660  sub getclasslist { Line 666  sub getclasslist {
     #      #
     my %sections;      my %sections;
     my %fullnames;      my %fullnames;
       my %passback;
     my ($cdom,$cnum,$partlist);      my ($cdom,$cnum,$partlist);
     if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {      if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {
         $cdom = $env{"course.$env{'request.course.id'}.domain"};          $cdom = $env{"course.$env{'request.course.id'}.domain"};
         $cnum = $env{"course.$env{'request.course.id'}.num"};          $cnum = $env{"course.$env{'request.course.id'}.num"};
         my $res_error;          my $res_error;
         ($partlist) = &response_type($symb,\$res_error);          ($partlist) = &response_type($symb,\$res_error);
       } elsif ($filterbypbid) {
           $cdom = $env{"course.$env{'request.course.id'}.domain"};
           $cnum = $env{"course.$env{'request.course.id'}.num"};
     }      }
     foreach my $student (keys(%$classlist)) {      foreach my $student (keys(%$classlist)) {
         my $end      =           my $end      = 
Line 752  sub getclasslist { Line 762  sub getclasslist {
                 }                  }
             }              }
         }          }
           if ($filterbypbid) {
               if (ref($possibles) eq 'HASH') {
                   unless (exists($possibles->{$student})) {
                       delete($classlist->{$student});
                       next;
                   }
               }
               my $udom =
                   $classlist->{$student}->[&Apache::loncoursedata::CL_SDOM()];
               my $uname =
                   $classlist->{$student}->[&Apache::loncoursedata::CL_SNAME()];
               if (($udom ne '') && ($uname ne '')) {
                   my %pbinfo = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',[$filterbypbid],$udom,$uname);
                   if (ref($pbinfo{$filterbypbid}) eq 'ARRAY') {
                       $passback{$student} = $pbinfo{$filterbypbid};
                   } else {
                       delete($classlist->{$student});
                       next;
                   }
               }
           }
  $section = ($section ne '' ? $section : 'none');   $section = ($section ne '' ? $section : 'none');
  if (&canview($section)) {   if (&canview($section)) {
     if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {      if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {
Line 767  sub getclasslist { Line 798  sub getclasslist {
  }   }
     }      }
     my @sections = sort(keys(%sections));      my @sections = sort(keys(%sections));
     return ($classlist,\@sections,\%fullnames);      return ($classlist,\@sections,\%fullnames,\%passback);
 }  }
   
 sub canmodify {  sub canmodify {
Line 1030  sub verifyreceipt { Line 1061  sub verifyreceipt {
     return $string;      return $string;
 }  }
   
   #-------------------------------------------------------------------
   
   #------------------------------------------- Grade Passback Routines
   #
   
   sub initialpassback {
       my ($request,$symb) = @_;
       my $cdom = $env{"course.$env{'request.course.id'}.domain"};
       my $cnum = $env{"course.$env{'request.course.id'}.num"};
       my $crstype = &Apache::loncommon::course_type();
       my %passback = &Apache::lonnet::dump('nohist_linkprot_passback',$cdom,$cnum);
       my $readonly;
       unless ($perm{'mgr'}) {
           $readonly = 1;
       }
       my $formname = 'initialpassback';
       my $navmap = Apache::lonnavmaps::navmap->new();
       my $output;
       if (!defined($navmap)) {
           if ($crstype eq 'Community') {
               $output = &mt('Unable to retrieve information about community contents');
           } else {
               $output = &mt('Unable to retrieve information about course contents');
           }
           return '<p>'.$output.'</p>';
       }
       return &Apache::loncourserespicker::create_picker($navmap,'passback',$formname,$crstype,undef,
                                                         undef,undef,undef,undef,undef,undef,
                                                         \%passback,$readonly);
   }
   
   sub passback_filters {
       my ($request,$symb) = @_;
       my $cdom = $env{"course.$env{'request.course.id'}.domain"};
       my $cnum = $env{"course.$env{'request.course.id'}.num"};
       my $crstype = &Apache::loncommon::course_type();
       my ($launcher,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);
       if ($env{'form.passback'} ne '') {
           $chosen = &unescape($env{'form.passback'});
           ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
           ($launcher,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);
       }
       my $result;
       if ($launcher ne '') {
           $result = &launcher_info_box($launcher,$appname,$setter,$linkuri,$scope).
                     '<p><br />'.&mt('Set criteria to use to list students for possible passback of scores, then push Next [_1]',
                                     '&rarr;').
                     '</p>';
       }
       $result .= '<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
                  '<input type="hidden" name="passback" value="'.&escape($chosen).'" />'."\n".
                  '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
       my ($submittext,$newcommand);
       if ($launcher ne '') {
           $submittext = &mt('Next').' &rarr;';
           $newcommand = 'passbacknames';
           $result .=  &selectfield(0)."\n";
       } else {
           $submittext = '&larr; '.&mt('Previous');
           $newcommand = 'initialpassback';
           if ($env{'form.passback'}) {
               $result .= '<span class="LC_warning">'.&mt('Invalid launcher').'</span>'."\n";
           } else {
               $result .= '<span class="LC_warning">'.&mt('No launcher selected').'</span>'."\n";
           }
       }
       $result .=  '<input type="hidden" name="command" value="'.$newcommand.'" />'."\n".
                   '<div>'."\n".
                   '<input type="submit" value="'.$submittext.'" />'."\n".
                   '</div>'."\n".
                   '</form>'."\n";
       return $result;
   }
   
   sub names_for_passback {
       my ($request,$symb) = @_;
       my $cdom = $env{"course.$env{'request.course.id'}.domain"};
       my $cnum = $env{"course.$env{'request.course.id'}.num"};
       my $crstype = &Apache::loncommon::course_type();
       my ($launcher,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);
       if ($env{'form.passback'} ne '') {
           $chosen = &unescape($env{'form.passback'});
           ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
           ($launcher,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);
       }
       my ($result,$ctr,$newcommand,$submittext);
       if ($launcher ne '') {
           $result = &launcher_info_box($launcher,$appname,$setter,$linkuri,$scope);
       }
       $ctr = 0;
       my @statuses = &Apache::loncommon::get_env_multiple('form.Status');
       my $stu_status = join(':',@statuses);
       $result .= '<form action="/adm/grades" method="post" name="passbackusers">'."\n".
                  '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
       if ($launcher ne '') {
           $result .= '<input type="hidden" name="passback" value="'.&escape($chosen).'" />'."\n".
                      '<input type="hidden" name="Status" value="'.$stu_status.'" />'."\n";
           my ($sections,$groups,$group_display,$disabled) = &sections_and_groups();
           my $section_display = join(' ',@{$sections});
           my $status_display;
           if ((grep(/^Any$/,@statuses)) ||
               (@statuses == 3)) {
               $status_display = &mt('Any');
           } else {
               $status_display = join(' '.&mt('or').' ',map { &mt($_); } @statuses);
           }
           $result .= '<p>'.&mt('Student(s) with stored passback credentials for [_1], and also satisfy:',
                                '<span class="LC_cusr_emph">'.$linkuri.'</span>').
                      '<ul>'.
                      '<li>'.&mt('Section(s)').": $section_display</li>\n".
                      '<li>'.&mt('Group(s)').": $group_display</li>\n".
                      '<li>'.&mt('Status').": $status_display</li>\n".
                      '</ul>';
           my ($classlist,undef,$fullname) = &getclasslist($sections,'1',$groups,'','','',$chosen);
           if (keys(%$fullname)) {
               $newcommand = 'passbackscores';
               $result .= &build_section_inputs().
                          &checkselect_js('passbackusers').
                          '<p><br />'.
                          &mt("To send scores, check box(es) next to the student's name(s), then push 'Send Scores'.").
                          '</p>'.
                          &check_script('passbackusers', 'stuinfo')."\n".
                          '<input type="button" '."\n".
                          'onclick="javascript:checkSelect(this.form.stuinfo);" '."\n".
                          'value="'.&mt('Send Scores').'" /> <br />'."\n".
                          &check_buttons()."\n".
                          &Apache::loncommon::start_data_table().
                          &Apache::loncommon::start_data_table_header_row();
               my $loop = 0;
               while ($loop < 2) {
                   $result .= '<th>'.&mt('No.').'</th><th>'.&mt('Select').'</th>'.
                              '<th>'.&nameUserString('header').'&nbsp;'.&mt('Section/Group').'</th>';
                   $loop++;
               }
               $result .= &Apache::loncommon::end_data_table_header_row()."\n";
               foreach my $student (sort
                                    {
                                        if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
                                            return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
                                        }
                                        return $a cmp $b;
                                    }
                                    (keys(%$fullname))) {
                   $ctr++;
                   my $section = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
                   my $group = $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
                   my $udom = $classlist->{$student}->[&Apache::loncoursedata::CL_SDOM()];
                   my $uname = $classlist->{$student}->[&Apache::loncoursedata::CL_SNAME()];
                   if ( $perm{'vgr'} eq 'F' ) {
                       if ($ctr%2 ==1) {
                           $result.= &Apache::loncommon::start_data_table_row();
                       }
                       $result .= '<td align="right">'.$ctr.'&nbsp;</td>'.
                                  '<td align="center"><label><input type="checkbox" name="stuinfo" value="'.
                                  $student.':'.$$fullname{$student}.':::SECTION'.$section.
                                  ')&nbsp;" />&nbsp;&nbsp;</label></td>'."\n".'<td>'.
                                  &nameUserString(undef,$$fullname{$student},$uname,$udom).
                                  '&nbsp;'.$section.($group ne '' ?'/'.$group:'').'</td>'."\n";
   
                       if ($ctr%2 ==0) {
                           $result .= &Apache::loncommon::end_data_table_row()."\n";
                       }
                   }
               }
               if ($ctr%2 ==1) {
                   $result .= &Apache::loncommon::end_data_table_row();
               }
               $result .= &Apache::loncommon::end_data_table()."\n";
               if ($ctr) {
                   $result .= '<input type="button" '.
                              'onclick="javascript:checkSelect(this.form.stuinfo);" '.
                              'value="'.&mt('Send Scores').'" />'."\n";
               }
           } else {
               $submittext = '&larr; '.&mt('Previous');
               $newcommand = 'passback';
               $result .= '<span class="LC_warning">'.&mt('No students match the selection criteria').'</p>';
           }
       } else {
           $newcommand = 'initialpassback';
           $submittext = &mt('Start over');
           if ($env{'form.passback'}) {
               $result .= '<span class="LC_warning">'.&mt('Invalid launcher').'</span>'."\n";
           } else {
               $result .= '<span class="LC_warning">'.&mt('No launcher selected').'</span>'."\n";
           }
       }
       $result .=  '<input type="hidden" name="command" value="'.$newcommand.'" />'."\n";
       if (!$ctr) {
           $result .= '<div>'."\n".
                      '<input type="submit" value="'.$submittext.'" />'."\n".
                      '</div>'."\n";
       }
       $result .= '</form>'."\n";
       return $result;
   }
   
   sub do_passback {
       my ($request,$symb) = @_;
       my $cdom = $env{"course.$env{'request.course.id'}.domain"};
       my $cnum = $env{"course.$env{'request.course.id'}.num"};
       my $crstype = &Apache::loncommon::course_type();
       my ($launcher,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);
       if ($env{'form.passback'} ne '') {
           $chosen = &unescape($env{'form.passback'});
           ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
           ($launcher,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);
       }
       if ($launcher ne '') {
           $request->print(&launcher_info_box($launcher,$appname,$setter,$linkuri,$scope));
       }
       my $error;
       if ($perm{'mgr'}) {
           if ($launcher ne '') {
               my @poss_students = &Apache::loncommon::get_env_multiple('form.stuinfo');
               if (@poss_students) {
                   my %possibles;
                   foreach my $item (@poss_students) {
                       my ($stuname,$studom) = split(/:/,$item,3);
                       $possibles{$stuname.':'.$studom} = 1;
                   }
                   my ($sections,$groups,$group_display,$disabled) = &sections_and_groups();
                   my ($classlist,undef,$fullname,$pbinfo) =
                       &getclasslist($sections,'1',$groups,'','','',$chosen,\%possibles);
                   if ((ref($classlist) eq 'HASH') && (ref($pbinfo) eq 'HASH')) {
                       my %passback = %{$pbinfo};
                       my (%tosend,%remotenotok,%scorenotok,%zeroposs,%nopbinfo);
                       foreach my $possible (keys(%possibles)) {
                           if ((exists($classlist->{$possible})) &&
                               (exists($passback{$possible})) && (ref($passback{$possible}) eq 'ARRAY')) {
                               $tosend{$possible} = 1;
                           }
                       }
                       if (keys(%tosend)) {
                           my ($lti_in_use,$crsdef);
                           my ($ltinum,$ltitype) = ($linkprotector =~ /^(\d+)(c|d)$/);
                           if ($ltitype eq 'c') {
                               my %crslti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
                               $lti_in_use = $crslti{$ltinum};
                               $crsdef = 1;
                           } else {
                               my %domlti = &Apache::lonnet::get_domain_lti($cdom,'linkprot');
                               $lti_in_use = $domlti{$ltinum};
                           }
                           if (ref($lti_in_use) eq 'HASH') {
                               my $msgformat = $lti_in_use->{'passbackformat'};
                               my $keynum = $lti_in_use->{'cipher'};
                               my $scoretype = 'decimal';
                               if ($lti_in_use->{'scoreformat'} =~ /^(decimal|ratio|percentage)$/) {
                                   $scoretype = $1;
                               }
                               my $pbsymb = &Apache::loncommon::symb_from_tinyurl($linkuri,$cnum,$cdom);
                               my $pbmap;
                               if ($pbsymb =~ /\.(page|sequence)$/) {
                                   $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pbsymb))[2]);
                               } else {
                                   $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pbsymb))[0]);
                               }
                               $pbmap = &Apache::lonnet::clutter($pbmap);
                               my $pbscope;
                               if ($scope eq 'res') {
                                   $pbscope = 'resource';
                               } elsif ($scope eq 'map') {
                                   $pbscope = 'nonrec';
                               } elsif ($scope eq 'rec') {
                                   $pbscope = 'map';
                               }
                               my %pb = &common_passback_info();
                               my $numstudents = scalar(keys(%tosend));
                               my %prog_state = &Apache::lonhtmlcommon::Create_PrgWin($request,$numstudents);
                               my $outcome = &Apache::loncommon::start_data_table().
                                            &Apache::loncommon::start_data_table_header_row();
                               my $loop = 0;
                               while ($loop < 2) {
                                   $outcome .= '<th>'.&mt('No.').'</th>'.
                                              '<th>'.&nameUserString('header').'&nbsp;'.&mt('Section/Group').'</th>'.
                                              '<th>'.&mt('Score').'</th>';
                                    $loop++;
                               }
                               $outcome .= &Apache::loncommon::end_data_table_header_row()."\n";
                               my $ctr=0;
                               foreach my $student (sort
                                   {
                                        if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
                                            return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
                                        }
                                        return $a cmp $b;
                                   } (keys(%$fullname))) {
                                   next unless ($tosend{$student});
                                   my ($uname,$udom) = split(/:/,$student);
                                   &Apache::lonhtmlcommon::Increment_PrgWin($request,\%prog_state,'last student');
                                   my ($uname,$udom) = split(/:/,$student);
                                   my $uhome = &Apache::lonnet::homeserver($uname,$udom),
                                   my $id = $passback{$student}[0],
                                   my $url = $passback{$student}[1],
                                   my ($total,$possible,$usec);
                                   if (ref($classlist->{$student}) eq 'ARRAY') {
                                       $usec = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION];
                                   }
                                   if ($pbscope eq 'resource') {
                                       $total = 0;
                                       $possible = 0;
                                       my $navmap = Apache::lonnavmaps::navmap->new($uname,$udom);
                                       if (ref($navmap)) {
                                           my $res = $navmap->getBySymb($pbsymb);
                                           if (ref($res)) {
                                               my $partlist = $res->parts();
                                               if (ref($partlist) eq 'ARRAY') {
                                                   my %record = &Apache::lonnet::restore($pbsymb,$env{'request.course.id'},$udom,$uname);
                                                   foreach my $part (@{$partlist}) {
                                                       next if ($record{"resource.$part.solved"} =~/^excused/);
                                                       my $weight = &Apache::lonnet::EXT("resource.$part.weight",$pbsymb,$udom,$uname,$usec);
                                                       $possible += $weight;
                                                       if (($record{'version'}) && (exists($record{"resource.$part.awarded"}))) {
                                                           my $awarded = $record{"resource.$part.awarded"};
                                                           if ($awarded) {
                                                               $total += $weight * $awarded;
                                                           }
                                                       }
                                                   }
                                               }
                                           }
                                       }
                                   } elsif (($pbscope eq 'map') || ($pbscope eq 'nonrec')) {
                                       ($total,$possible) =
                                           &Apache::lonhomework::get_lti_score($uname,$udom,$pbmap,$pbscope);
                                   }
                                   if (($id ne '') && ($url ne '') && ($possible)) {
                                       my ($sent,$score,$code,$result) =
                                           &LONCAPA::ltiutils::send_grade($cdom,$cnum,$crsdef,$pb{'type'},$ltinum,$keynum,$id,
                                                                          $url,$scoretype,$pb{'sigmethod'},$msgformat,$total,$possible);
                                       my $no_passback;
                                       if ($sent) {
                                           if ($code == 200) {
                                               delete($tosend{$student});
                                               my $namespace = $cdom.'_'.$cnum.'_lp_passback';
                                               my $store = {
                                                    'score' => $score,
                                                    'ip' => $pb{'ip'},
                                                    'host' => $pb{'lonhost'},
                                                    'protector' => $linkprotector,
                                                    'deeplink' => $linkuri,
                                                    'scope' => $scope,
                                                    'url' => $url,
                                                    'id' => $id,
                                                    'clientip' => $pb{'clientip'},
                                                    'whodoneit' => $env{'user.name'}.':'.$env{'user.domain'},
                                                   };
                                               my $value='';
                                               foreach my $key (keys(%{$store})) {
                                                   $value.=&escape($key).'='.&Apache::lonnet::freeze_escape($store->{$key}).'&';
                                               }
                                               $value=~s/\&$//;
                                               &Apache::lonnet::courselog(&escape($linkuri).':'.$uname.':'.$udom.':EXPORT:'.$value);
                                               &Apache::lonnet::cstore({'score' => $score},$chosen,$namespace,$udom,$uname,'',$pb{'ip'},1);
                                               $ctr++;
                                               if ($ctr%2 ==1) {
                                                   $outcome .= &Apache::loncommon::start_data_table_row();
                                               }
                                               my $section = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
                                               my $group = $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
                                               $outcome .= '<td align="right">'.$ctr.'&nbsp;</td>'.
                                                          '<td>'.&nameUserString(undef,$$fullname{$student},$uname,$udom).
                                                          '&nbsp;'.$section.($group ne '' ?'/'.$group:'').'</td>'.
                                                          '<td>'.$score.'</td>'."\n";
                                               if ($ctr%2 ==0) {
                                                   $outcome .= &Apache::loncommon::end_data_table_row()."\n";
                                               }
                                           } else {
                                               $remotenotok{$student} = 1;
                                               $no_passback = "Passback response for ".$linkprotector." was $code ($result)";
                                               &Apache::lonnet::logthis($no_passback." for $uname:$udom in ${cdom}_${cnum}");
                                           }
                                       } else {
                                           $scorenotok{$student} = 1;
                                           $no_passback = "Passback of grades not sent for ".$linkprotector;
                                           &Apache::lonnet::logthis($no_passback." for $uname:$udom in ${cdom}_${cnum}");
                                       }
                                       if ($no_passback) {
                                           &Apache::lonnet::log($udom,$uname,$uhome,$no_passback." score: $score; total: $total; possible: $possible");
                                           my $ltigrade = {
                                               'ltinum'   => $ltinum,
                                               'lti'      => $lti_in_use,
                                               'crsdef'   => $crsdef,
                                               'cid'      => $cdom.'_'.$cnum,
                                               'uname'    => $uname,
                                               'udom'     => $udom,
                                               'uhome'    => $uhome,
                                               'pbid'     => $id,
                                               'pburl'    => $url,
                                               'pbtype'   => $pb{'type'},
                                               'pbscope'  => $pbscope,
                                               'pbmap'    => $pbmap,
                                               'pbsymb'   => $pbsymb,
                                               'format'   => $scoretype,
                                               'scope'    => $scope,
                                               'clientip' => $pb{'clientip'},
                                               'linkprot' => $linkprotector.':'.$linkuri,
                                               'total'    => $total,
                                               'possible' => $possible,
                                               'score'    => $score,
                                           };
                                           &Apache::lonnet::put('linkprot_passback_pending',$ltigrade,$cdom,$cnum);
                                       }
                                   } else {
                                       if (($id ne '') && ($url ne '')) {
                                           $zeroposs{$student} = 1;
                                       } else {
                                           $nopbinfo{$student} = 1;
                                       }
                                   }
                               }
                               &Apache::lonhtmlcommon::Close_PrgWin($request,\%prog_state);
                               if ($ctr%2 ==1) {
                                   $outcome .= &Apache::loncommon::end_data_table_row();
                               }
                               $outcome .= &Apache::loncommon::end_data_table();
                               if ($ctr) {
                                   $request->print('<p><br />'.&mt('Scores sent to launcher CMS').'</p>'.
                                                   '<p>'.$outcome.'</p>');
                               } else {
                                   $request->print('<p>'.&mt('No scores sent to launcher CMS').'</p>');
                               }
                               if (keys(%tosend)) {
                                   $request->print('<p>'.&mt('No scores sent for following'));
                                   my ($zeros,$nopbcreds,$noconfirm,$noscore);
                                   foreach my $student (sort
                                   {
                                        if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
                                            return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
                                        }
                                        return $a cmp $b;
                                   } (keys(%$fullname))) {
                                       next unless ($tosend{$student});
                                       my ($uname,$udom) = split(/:/,$student);
                                       my $line = '<li>'.&nameUserString(undef,$$fullname{$student},$uname,$udom).'</li>'."\n";
                                       if ($zeroposs{$student}) {
                                           $zeros .= $line;
                                       } elsif ($nopbinfo{$student}) {
                                           $nopbcreds .= $line;
                                       } elsif ($remotenotok{$student}) {
                                           $noconfirm .= $line;
                                       } elsif ($scorenotok{$student}) {
                                           $noscore .= $line;
                                       }
                                   }
                                   if ($zeros) {
                                       $request->print('<br />'.&mt('Total points possible was 0').':'.
                                                       '<ul>'.$zeros.'</ul><br />');
                                   }
                                   if ($nopbcreds) {
                                       $request->print('<br />'.&mt('Missing unique identifier and/or passback location').':'.
                                                       '<ul>'.$nopbcreds.'</ul><br />');
                                   }
                                   if ($noconfirm) {
                                       $request->print('<br />'.&mt('Score receipt not confirmed by receiving CMS').':'.
                                                       '<ul>'.$noconfirm.'</ul><br />');
                                   }
                                   if ($noscore) {
                                       $request->print('<br />'.&mt('Score computation or transmission failed').':'.
                                                       '<ul>'.$noscore.'</ul><br />');
                                   }
                                   $request->print('</p>');
                               }
                           } else {
                               $error = &mt('Settings for deep-link launch target unavailable, so no scores were sent');
                           }
                       } else {
                           $error = &mt('No available students for whom scores can be sent.');
                       }
                   } else {
                       $error = &mt('Classlist could not be retrieved so no scores were sent.');
                   }
               } else {
                   $error = &mt('No students selected to receive scores so none were sent.');
               }
           } else {
               if ($env{'form.passback'}) {
                   $error = &mt('Deep-link launch target was invalid so no scores were sent.');
               } else {
                   $error = &mt('Deep-link launch target was missing so no scores were sent.');
               }
           }
       } else {
           $error = &mt('You do not have permission to manage grades, so no scores were sent');
       }
       if ($error) {
           $request->print('<p class="LC_info">'.$error.'</p>');
       }
       return;
   }
   
   sub get_passback_launcher {
       my ($cdom,$cnum,$chosen) = @_;
       my ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
       my ($ltinum,$ltitype) = ($linkprotector =~ /^(\d+)(c|d)$/);
       my ($appname,$setter);
       if ($ltitype eq 'c') {
           my %lti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
           if (ref($lti{$ltinum}) eq 'HASH') {
               $appname = $lti{$ltinum}{'name'};
               if ($appname) {
                   $setter = ' (defined in course)';
               }
           }
       } elsif ($ltitype eq 'd') {
           my %lti = &Apache::lonnet::get_domain_lti($cdom,'linkprot');
           if (ref($lti{$ltinum}) eq 'HASH') {
               $appname = $lti{$ltinum}{'name'};
               if ($appname) {
                   $setter = ' (defined in domain)';
               }
           }
       }
       if ($linkuri =~ m{^\Q/tiny/$cdom/\E(\w+)$}) {
           my $key = $1;
           my $tinyurl;
           my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key);
           if (defined($cached)) {
               $tinyurl = $result;
           } else {
               my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
               my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname);
               if ($currtiny{$key} ne '') {
                   $tinyurl = $currtiny{$key};
                   &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600);
               }
           }
           if ($tinyurl) {
               my ($crsnum,$launchsymb) = split(/\&/,$tinyurl);
               if ($crsnum eq $cnum) {
                   my %passback = &Apache::lonnet::get('nohist_linkprot_passback',[$launchsymb],$cdom,$cnum);
                   if (ref($passback{$launchsymb}) eq 'HASH') {
                       if (exists($passback{$launchsymb}{$chosen})) {
                           return ($launchsymb,$appname,$setter);
                       }
                   }
               }
           }
       }
       return ();
   }
   
   sub sections_and_groups {
       my (@sections,@groups,$group_display);
       @groups = &Apache::loncommon::get_env_multiple('form.group');
       if (grep(/^all$/,@groups)) {
            @groups = ('all');
            $group_display = 'all';
       } elsif (grep(/^none$/,@groups)) {
            @groups = ('none');
            $group_display = 'none';
       } elsif (@groups > 0) {
            $group_display = join(', ',@groups);
       }
       if ($env{'request.course.sec'} ne '') {
           @sections = ($env{'request.course.sec'});
       } else {
           @sections = &Apache::loncommon::get_env_multiple('form.section');
       }
       my $disabled = ' disabled="disabled"';
       if ($perm{'mgr'}) {
           if (grep(/^all$/,@sections)) {
               undef($disabled);
           } else {
               foreach my $sec (@sections) {
                   if (&canmodify($sec)) {
                       undef($disabled);
                       last;
                   }
               }
           }
       }
       if (grep(/^all$/,@sections)) {
           @sections = ('all');
       }
       return(\@sections,\@groups,$group_display,$disabled);
   }
   
   sub launcher_info_box {
       my ($launcher,$appname,$setter,$linkuri,$scope) = @_;
       my $shownscope;
       if ($scope eq 'res') {
           $shownscope = &mt('Resource');
       } elsif ($scope eq 'map') {
           $shownscope = &mt('Folder');
       }  elsif ($scope eq 'rec') {
           $shownscope = &mt('Folder + sub-folders');
       }
       return '<p>'.
              &Apache::lonhtmlcommon::start_pick_box().
              &Apache::lonhtmlcommon::row_title(&mt('Launch Item Title')).
              &Apache::lonnet::gettitle($launcher).
              &Apache::lonhtmlcommon::row_closure().
              &Apache::lonhtmlcommon::row_title(&mt('Deep-link')).
              $linkuri.
              &Apache::lonhtmlcommon::row_closure().
              &Apache::lonhtmlcommon::row_title(&mt('Launcher')).
              $appname.' '.$setter.
              &Apache::lonhtmlcommon::row_closure().
              &Apache::lonhtmlcommon::row_title(&mt('Score Type')).
              $shownscope.      
              &Apache::lonhtmlcommon::row_closure(1).
              &Apache::lonhtmlcommon::end_pick_box().'</p>'."\n";
   }
   
   sub passbacks_for_symb {
       my ($cdom,$cnum,$symb) = @_;
       my %passback = &Apache::lonnet::dump('nohist_linkprot_passback',$cdom,$cnum);
       my %needpb;
       if (keys(%passback)) {
           my $checkpb = 1;
           if (exists($passback{$symb})) {
               if (keys(%passback) == 1) {
                   undef($checkpb);
               }
               if (ref($passback{$symb}) eq 'HASH') {
                   foreach my $launcher (keys(%{$passback{$symb}})) {
                       $needpb{$launcher} = 1;
                   }
               }
           }
           if ($checkpb) {
               my ($map,$id,$url) = &Apache::lonnet::decode_symb($symb);
               my $navmap = Apache::lonnavmaps::navmap->new();
               if (ref($navmap)) {
                   my $mapres = $navmap->getResourceByUrl($map);
                   if (ref($mapres)) {
                       my $mapsymb = $mapres->symb();
                       if (exists($passback{$mapsymb})) {
                           if (keys(%passback) == 1) {
                               undef($checkpb);
                           }
                           if (ref($passback{$mapsymb}) eq 'HASH') {
                               foreach my $launcher (keys(%{$passback{$mapsymb}})) {
                                   $needpb{$launcher} = 1;
                               }
                           }
                       }
                       my %posspb;
                       if ($checkpb) {
                           my @recurseup = $navmap->recurseup_maps($map,1);
                           if (@recurseup) {
                               map { $posspb{$_} = 1; } @recurseup;
                           }
                       }
                       foreach my $key (keys(%passback)) {
                           if (exists($posspb{$key})) {
                               if (ref($passback{$key}) eq 'HASH') {
                                   foreach my $launcher (keys(%{$passback{$key}})) {
                                       my ($linkuri,$linkprotector,$scope) = split("\0",$launcher);
                                       next unless ($scope eq 'rec');
                                       $needpb{$launcher} = 1;
                                   }
                               }
                           }
                       }
                   }
               }
           }
       }
       return %needpb;
   }
   
   sub process_passbacks {
       my ($context,$symbs,$cdom,$cnum,$udom,$uname,$weights,$awardeds,$excuseds,$needpb,
           $skip_passback,$pbsave,$pbids) = @_;
       if ((ref($needpb) eq 'HASH') && (ref($skip_passback) eq 'HASH') && (ref($pbsave) eq 'HASH')) {
           my (%weight,%awarded,%excused);
           if ((ref($symbs) eq 'ARRAY') && (ref($weights) eq 'HASH') && (ref($awardeds) eq 'HASH') &&
               (ref($excuseds) eq 'HASH')) {
               %weight = %{$weights};
               %awarded = %{$awardeds};
               %excused = %{$excuseds};
           }
           my $uhome = &Apache::lonnet::homeserver($uname,$udom);
           my @launchers = keys(%{$needpb});
           my %pbinfo;
           if (ref($pbids) eq 'HASH') {
               %pbinfo = %{$pbids};
           } else {
               %pbinfo = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',\@launchers,$udom,$uname);
           }
           my %pbc = &common_passback_info();
           foreach my $launcher (@launchers) {
               if (ref($pbinfo{$launcher}) eq 'ARRAY') {
                   my $pbid = $pbinfo{$launcher}[0];
                   my $pburl = $pbinfo{$launcher}[1];
                   my (%total_by_symb,%possible_by_symb);
                   if (($pbid ne '') && ($pburl ne '')) {
                       next if ($skip_passback->{$launcher});
                       my %pb = %pbc;
                       if ((exists($pbsave->{$launcher})) &&
                           (ref($pbsave->{$launcher}) eq 'HASH')) {
                           foreach my $item ('lti_in_use','crsdef','ltinum','keynum','scoretype','msgformat',
                                             'symb','map','pbscope','linkuri','linkprotector','scope') {
                               $pb{$item} = $pbsave->{$launcher}{$item};
                           }
                       } else {
                           my $ltitype;
                           ($pb{'linkuri'},$pb{'linkprotector'},$pb{'scope'}) = split("\0",$launcher);
                           ($pb{'ltinum'},$ltitype) = ($pb{'linkprotector'} =~ /^(\d+)(c|d)$/);
                           if ($ltitype eq 'c') {
                               my %crslti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
                               $pb{'lti_in_use'} = $crslti{$pb{'ltinum'}};
                               $pb{'crsdef'} = 1;
                           } else {
                               my %domlti = &Apache::lonnet::get_domain_lti($cdom,'linkprot');
                               $pb{'lti_in_use'} = $domlti{$pb{'ltinum'}};
                           }
                           if (ref($pb{'lti_in_use'}) eq 'HASH') {
                               $pb{'msgformat'} = $pb{'lti_in_use'}->{'passbackformat'};
                               $pb{'keynum'} = $pb{'lti_in_use'}->{'cipher'};
                               $pb{'scoretype'} = 'decimal';
                               if ($pb{'lti_in_use'}->{'scoreformat'} =~ /^(decimal|ratio|percentage)$/) {
                                   $pb{'scoretype'} = $1;
                               }
                               $pb{'symb'} = &Apache::loncommon::symb_from_tinyurl($pb{'linkuri'},$cnum,$cdom);
                               if ($pb{'symb'} =~ /\.(page|sequence)$/) {
                                   $pb{'map'} = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pb{'symb'}))[2]);
                               } else {
                                   $pb{'map'} = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pb{'symb'}))[0]);
                               }
                               $pb{'map'} = &Apache::lonnet::clutter($pb{'map'});
                               if ($pb{'scope'} eq 'res') {
                                   $pb{'pbscope'} = 'resource';
                               } elsif ($pb{'scope'} eq 'map') {
                                   $pb{'pbscope'} = 'nonrec';
                               } elsif ($pb{'scope'} eq 'rec') {
                                   $pb{'pbscope'} = 'map';
                               }
                               foreach my $item ('lti_in_use','crsdef','ltinum','keynum','scoretype','msgformat',
                                                 'symb','map','pbscope','linkuri','linkprotector','scope') {
                                   $pbsave->{$launcher}{$item} = $pb{$item};
                               }
                           } else {
                               $skip_passback->{$launcher} = 1;
                           }
                       }
                       if (ref($symbs) eq 'ARRAY') {
                           foreach my $symb (@{$symbs}) {
                               if ((ref($weight{$symb}) eq 'HASH') && (ref($awarded{$symb}) eq 'HASH') &&
                                   (ref($excused{$symb}) eq 'HASH')) {
                                   foreach my $part (keys(%{$weight{$symb}})) {
                                       if ($excused{$symb}{$part}) {
                                           next;
                                       }
                                       my $partweight = $weight{$symb}{$part} eq '' ? 1 :
                                                        $weight{$symb}{$part};
                                       if ($awarded{$symb}{$part}) {
                                           $total_by_symb{$symb} += $partweight * $awarded{$symb}{$part};
                                       }
                                       $possible_by_symb{$symb} += $partweight;
                                   }
                               }
                           }
                       }
                       if ($context eq 'updatebypage') {
                           my $ltigrade = {
                                           'ltinum'     => $pb{'ltinum'},
                                           'lti'        => $pb{'lti_in_use'},
                                           'crsdef'     => $pb{'crsdef'},
                                           'cid'        => $cdom.'_'.$cnum,
                                           'uname'      => $uname,
                                           'udom'       => $udom,
                                           'uhome'      => $uhome,
                                           'pbid'       => $pbid,
                                           'pburl'      => $pburl,
                                           'pbtype'     => $pb{'type'},
                                           'pbscope'    => $pb{'pbscope'},
                                           'pbmap'      => $pb{'map'},
                                           'pbsymb'     => $pb{'symb'},
                                           'format'     => $pb{'scoretype'},
                                           'scope'      => $pb{'scope'},
                                           'clientip'   => $pb{'clientip'},
                                           'linkprot'   => $pb{'linkprotector'}.':'.$pb{'linkuri'},
                                           'total_s'    => \%total_by_symb,
                                           'possible_s' => \%possible_by_symb,
                           };
                           push(@Apache::grades::ltipassback,$ltigrade);
                           next;
                       }
                       my ($total,$possible);
                       if ($pb{'pbscope'} eq 'resource') {
                           $total = $total_by_symb{$pb{'symb'}};
                           $possible = $possible_by_symb{$pb{'symb'}};
                       } elsif (($pb{'pbscope'} eq 'map') || ($pb{'pbscope'} eq 'nonrec')) {
                           ($total,$possible) =
                               &Apache::lonhomework::get_lti_score($uname,$udom,$pb{'map'},$pb{'pbscope'},
                                                                   \%total_by_symb,\%possible_by_symb);
                       }
                       if (!$possible) {
                           $total = 0;
                           $possible = 1;
                       }
                       my ($sent,$score,$code,$result) =
                           &LONCAPA::ltiutils::send_grade($cdom,$cnum,$pb{'crsdef'},$pb{'type'},$pb{'ltinum'},
                                                          $pb{'keynum'},$pbid,$pburl,$pb{'scoretype'},$pb{'sigmethod'},
                                                          $pb{'msgformat'},$total,$possible);
                       my $no_passback;
                       if ($sent) {
                           if ($code == 200) {
                               my $namespace = $cdom.'_'.$cnum.'_lp_passback';
                               my $store = {
                                   'score' => $score,
                                   'ip' => $pb{'ip'},
                                   'host' => $pb{'lonhost'},
                                   'protector' => $pb{'linkprotector'},
                                   'deeplink' => $pb{'linkuri'},
                                   'scope' => $pb{'scope'},
                                   'url' => $pburl,
                                   'id' => $pbid,
                                   'clientip' => $pb{'clientip'},
                                   'whodoneit' => $env{'user.name'}.':'.$env{'user.domain'},
                               };
                               my $value='';
                               foreach my $key (keys(%{$store})) {
                                    $value.=&escape($key).'='.&Apache::lonnet::freeze_escape($store->{$key}).'&';
                               }
                               $value=~s/\&$//;
                               &Apache::lonnet::courselog(&escape($pb{'linkuri'}).':'.$uname.':'.$udom.':EXPORT:'.$value);
                               &Apache::lonnet::cstore({'score' => $score},$launcher,$namespace,$udom,$uname,'',$pb{'ip'},1);
                           } else {
                               $no_passback = 1;
                           }
                       } else {
                           $no_passback = 1;
                       }
                       if ($no_passback) {
                           &Apache::lonnet::log($udom,$uname,$uhome,$no_passback." score: $score; total: $total; possible: $possible");
                           my $ltigrade = {
                              'ltinum'   => $pb{'ltinum'},
                              'lti'      => $pb{'lti_in_use'},
                              'crsdef'   => $pb{'crsdef'},
                              'cid'      => $cdom.'_'.$cnum,
                              'uname'    => $uname,
                              'udom'     => $udom,
                              'uhome'    => $uhome,
                              'pbid'     => $pbid,
                              'pburl'    => $pburl,
                              'pbtype'   => $pb{'type'},
                              'pbscope'  => $pb{'pbscope'},
                              'pbmap'    => $pb{'map'},
                              'pbsymb'   => $pb{'symb'},
                              'format'   => $pb{'scoretype'},
                              'scope'    => $pb{'scope'},
                              'clientip' => $pb{'clientip'},
                              'linkprot' => $pb{'linkprotector'}.':'.$pb{'linkuri'},
                              'total'    => $total,
                              'possible' => $possible,
                              'score'    => $score,
                           };
                           &Apache::lonnet::put('linkprot_passback_pending',$ltigrade,$cdom,$cnum);
                       }
                   }
               }
           }
       }
       return;
   }
   
   sub common_passback_info {
       my %pbc = (
                  sigmethod => 'HMAC-SHA1',
                  type      => 'linkprot',
                  clientip  => &Apache::lonnet::get_requestor_ip(),
                  lonhost   => $Apache::lonnet::perlvar{'lonHostID'},
                  ip        => &Apache::lonnet::get_host_ip($Apache::lonnet::perlvar{'lonHostID'}),
                );
       return %pbc;
   }
   
 #--- This is called by a number of programs.  #--- This is called by a number of programs.
 #--- Called from the Grading Menu - View/Grade an individual student  #--- Called from the Grading Menu - View/Grade an individual student
 #--- Also called directly when one clicks on the subm button   #--- Also called directly when one clicks on the subm button 
Line 1037  sub verifyreceipt { Line 1940  sub verifyreceipt {
 sub listStudents {  sub listStudents {
     my ($request,$symb,$submitonly,$divforres) = @_;      my ($request,$symb,$submitonly,$divforres) = @_;
   
       my $is_tool   = ($symb =~ /ext\.tool$/);
     my $cdom      = $env{"course.$env{'request.course.id'}.domain"};      my $cdom      = $env{"course.$env{'request.course.id'}.domain"};
     my $cnum      = $env{"course.$env{'request.course.id'}.num"};      my $cnum      = $env{"course.$env{'request.course.id'}.num"};
     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
Line 1060  sub listStudents { Line 1964  sub listStudents {
         }          }
     }      }
   
     my %js_lt = &Apache::lonlocal::texthash (      $request->print(&checkselect_js());
  'multiple' => 'Please select a student or group of students before clicking on the Next button.',  
  'single'   => 'Please select the student before clicking on the Next button.',  
      );  
     &js_escape(\%js_lt);  
     $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT));      $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT));
     function checkSelect(checkBox) {  
  var ctr=0;  
  var sense="";  
  if (checkBox.length > 1) {  
     for (var i=0; i<checkBox.length; i++) {  
  if (checkBox[i].checked) {  
     ctr++;  
  }  
     }  
     sense = '$js_lt{'multiple'}';  
  } else {  
     if (checkBox.checked) {  
  ctr = 1;  
     }  
     sense = '$js_lt{'single'}';  
  }  
  if (ctr == 0) {  
     alert(sense);  
     return false;  
  }  
  document.gradesub.submit();  
     }  
   
     function reLoadList(formname) {      function reLoadList(formname) {
  if (formname.saveStatusOld.value == pullDownSelection(formname.Status)) {return;}   if (formname.saveStatusOld.value == pullDownSelection(formname.Status)) {return;}
Line 1103  LISTJAVASCRIPT Line 1981  LISTJAVASCRIPT
  "\n".$table;   "\n".$table;
   
     $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();      $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))      unless ($is_tool) {
                   .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"          $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
                   .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"                        .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"
                   .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"                        .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"
                   .&Apache::lonhtmlcommon::row_closure();                        .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))                        .&Apache::lonhtmlcommon::row_closure();
                   .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"          $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))
                   .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n"                        .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"
                   .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"                        .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n"
                   .&Apache::lonhtmlcommon::row_closure();                        .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"
                         .&Apache::lonhtmlcommon::row_closure();
       }
   
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));      my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
     my $saveStatus = $stu_status eq '' ? 'Active' : $stu_status;      my $saveStatus = $stu_status eq '' ? 'Active' : $stu_status;
     $env{'form.Status'} = $saveStatus;      $env{'form.Status'} = $saveStatus;
     my %optiontext = &Apache::lonlocal::texthash (      my %optiontext;
       if ($is_tool) {
           %optiontext = &Apache::lonlocal::texthash (
                             lastonly => 'last transaction',
                             last     => 'last transaction with details',
                             datesub  => 'all transactions',
                             all      => 'all transactions with details',
                         );
       } else {
           %optiontext = &Apache::lonlocal::texthash (
                           lastonly => 'last submission',                            lastonly => 'last submission',
                           last     => 'last submission with details',                            last     => 'last submission with details',
                           datesub  => 'all submissions',                            datesub  => 'all submissions',
                           all      => 'all submissions with details',                            all      => 'all submissions with details',
                       );                        );
       }
     my $submission_options =      my $submission_options =
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="lastonly" /> '.          '<label><input type="radio" name="lastSub" value="lastonly" /> '.
Line 1136  LISTJAVASCRIPT Line 2026  LISTJAVASCRIPT
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="all" /> '.          '<label><input type="radio" name="lastSub" value="all" /> '.
         $optiontext{'all'}.'</label></span>';          $optiontext{'all'}.'</label></span>';
       my $viewtitle;
       if ($is_tool) {
           $viewtitle = &mt('View Transactions');
       } else {
           $viewtitle = &mt('View Submissions');
       }
     my ($compmsg,$nocompmsg);      my ($compmsg,$nocompmsg);
     $nocompmsg = ' checked="checked"';      $nocompmsg = ' checked="checked"';
     if ($numessay) {      if ($numessay) {
         $compmsg = $nocompmsg;          $compmsg = $nocompmsg;
         $nocompmsg = '';          $nocompmsg = '';
     }      }
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Submissions'))      $gradeTable .= &Apache::lonhtmlcommon::row_title($viewtitle)
                   .$submission_options;                    .$submission_options;
 # Check if any gradable  # Check if any gradable
     my $showmore;      my $showmore;
Line 1207  LISTJAVASCRIPT Line 2103  LISTJAVASCRIPT
     }      }
     $gradeTable .= &Apache::lonhtmlcommon::row_closure(1)      $gradeTable .= &Apache::lonhtmlcommon::row_closure(1)
                   .&Apache::lonhtmlcommon::end_pick_box();                    .&Apache::lonhtmlcommon::end_pick_box();
       my $regrademsg;
       if ($is_tool) {
           $regrademsg =&mt("To view/grade/regrade, click on the check box(es) next to the student's name(s). Then click on the Next button.");
       } else {
           $regrademsg = &mt("To view/grade/regrade a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.");
       }
     $gradeTable .= '<p>'      $gradeTable .= '<p>'
                   .&mt("To view/grade/regrade a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.")."\n"                    .$regrademsg."\n"
                   .'<input type="hidden" name="command" value="processGroup" />'                    .'<input type="hidden" name="command" value="processGroup" />'
                   .'</p>';                    .'</p>';
   
Line 1352  LISTJAVASCRIPT Line 2253  LISTJAVASCRIPT
     return '';      return '';
 }  }
   
 #---- Called from the listStudents routine  #---- Called from the listStudents and the names_for_passback routines.
   
   sub checkselect_js {
       my ($formname) = @_;
       if ($formname eq '') {
           $formname = 'gradesub';
       }
       my %js_lt;
       if ($formname eq 'passbackusers') {
           %js_lt = &Apache::lonlocal::texthash (
                        'multiple' => 'Please select a student or group of students before pushing the Save Scores button.',
                        'single'   => 'Please select the student before pushing the Save Scores button.',
                    );
       } else {
           %js_lt = &Apache::lonlocal::texthash (
                        'multiple' => 'Please select a student or group of students before clicking on the Next button.',
                        'single'   => 'Please select the student before clicking on the Next button.',
                    );
       }
       &js_escape(\%js_lt);
       return &Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT);
   
       function checkSelect(checkBox) {
           var ctr=0;
           var sense="";
           var len = checkBox.length;
           if (len == undefined) len = 1;
           if (len > 1) {
               for (var i=0; i<len; i++) {
                   if (checkBox[i].checked) {
                       ctr++;
                   }
               }
               sense = '$js_lt{'multiple'}';
           } else {
               if (checkBox.checked) {
                   ctr = 1;
               }
               sense = '$js_lt{'single'}';
           }
           if (ctr == 0) {
               alert(sense);
               return false;
           }
           document.$formname.submit();
       }
   LISTJAVASCRIPT
   
   }
   
 sub check_script {  sub check_script {
     my ($form,$type) = @_;      my ($form,$type) = @_;
Line 1644  INNERJS Line 2593  INNERJS
     my $end_page_msg_central =      my $end_page_msg_central =
  &Apache::loncommon::end_page({'js_ready' => 1});   &Apache::loncommon::end_page({'js_ready' => 1});
   
   
     my $docopen=&Apache::lonhtmlcommon::javascript_docopen();      my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
     $docopen=~s/^document\.//;      $docopen=~s/^document\.//;
   
Line 1772  INNERJS Line 2720  INNERJS
   
   function msgTail() {    function msgTail() {
     pDoc = pWin.document;      pDoc = pWin.document;
       //pDoc.write("<\\/table>");
     pDoc.write("<\\/td><\\/tr><\\/table>&nbsp;");      pDoc.write("<\\/td><\\/tr><\\/table>&nbsp;");
     pDoc.write("<input type=\\"button\\" value=\\"$html_js_lt{'save'}\\" onclick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");      pDoc.write("<input type=\\"button\\" value=\\"$html_js_lt{'save'}\\" onclick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
     pDoc.write("<input type=\\"button\\" value=\\"$html_js_lt{'canc'}\\" onclick=\\"self.close()\\"><br /><br />");      pDoc.write("<input type=\\"button\\" value=\\"$html_js_lt{'canc'}\\" onclick=\\"self.close()\\"><br /><br />");
Line 2091  sub handback_box { Line 3040  sub handback_box {
     if ($file =~ /\/portfolio\//) {      if ($file =~ /\/portfolio\//) {
                 $file_counter++;                  $file_counter++;
            my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|);             my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|);
            my ($name,$version,$ext) = &file_name_version_ext($file_disp);             my ($name,$version,$ext) = &Apache::lonnet::file_name_version_ext($file_disp);
            $file_disp = "$name.$ext";             $file_disp = "$name.$ext";
            $file = $file_path.$file_disp;             $file = $file_path.$file_disp;
            $result.=&mt('Return commented version of [_1] to student.',             $result.=&mt('Return commented version of [_1] to student.',
Line 2178  sub files_exist { Line 3127  sub files_exist {
         my ($uname,$udom,$fullname) = split(/:/,$student);          my ($uname,$udom,$fullname) = split(/:/,$student);
         my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},          my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},
       $udom,$uname);        $udom,$uname);
         my ($string,$timestamp)= &get_last_submission(\%record);          my ($string)= &get_last_submission(\%record);
         foreach my $submission (@$string) {          foreach my $submission (@$string) {
             my ($partid,$respid) =              my ($partid,$respid) =
  ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);   ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
Line 2281  sub submission { Line 3230  sub submission {
   
     if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }      if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }
     my $probtitle=&Apache::lonnet::gettitle($symb);      my $probtitle=&Apache::lonnet::gettitle($symb);
       my $is_tool = ($symb =~ /ext\.tool$/);
     my ($essayurl,%coursedesc_by_cid);      my ($essayurl,%coursedesc_by_cid);
   
     if (!&canview($usec)) {      if (!&canview($usec)) {
Line 2302  sub submission { Line 3252  sub submission {
     }      }
   
     if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }      if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }
     if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }      unless ($is_tool) { 
     if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }          if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }
           if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }
       }
     if (($numessay) && ($calledby eq 'submission') && (!exists($env{'form.compmsg'}))) {      if (($numessay) && ($calledby eq 'submission') && (!exists($env{'form.compmsg'}))) {
         $env{'form.compmsg'} = 1;          $env{'form.compmsg'} = 1;
     }      }
Line 2496  sub submission { Line 3448  sub submission {
     # Display student info      # Display student info
     $request->print(($counter == 0 ? '' : '<br />'));      $request->print(($counter == 0 ? '' : '<br />'));
   
       my $boxtitle = &mt('Submissions');
       if ($is_tool) {
           $boxtitle = &mt('Transactions')
       }
     my $result='<div class="LC_Box">'      my $result='<div class="LC_Box">'
               .'<h3 class="LC_hcell">'.&mt('Submissions').'</h3>';                .'<h3 class="LC_hcell">'.$boxtitle.'</h3>';
     $result.='<input type="hidden" name="name'.$counter.      $result.='<input type="hidden" name="name'.$counter.
              '" value="'.$env{'form.fullname'}.'" />'."\n";               '" value="'.$env{'form.fullname'}.'" />'."\n";
     if ($numresp > $numessay) {      if (($numresp > $numessay) && !$is_tool) {
         $result.='<p class="LC_info">'          $result.='<p class="LC_info">'
                 .&mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)                  .&mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)
                 ."</p>\n";                  ."</p>\n";
Line 2523  sub submission { Line 3479  sub submission {
     #             (3) All transactions (by date)      #             (3) All transactions (by date)
     #             (4) The whole record (with detailed information for all transactions)      #             (4) The whole record (with detailed information for all transactions)
   
     my ($string,$timestamp)= &get_last_submission(\%record);      my ($lastsubonly,$partinfo) =
           &show_last_submission($uname,$udom,$symb,$essayurl,$responseType,$env{'form.lastSub'},
     my $lastsubonly;                                $is_tool,$fullname,\%record,\%coursedesc_by_cid);
       $request->print($partinfo);
     if ($$timestamp eq '') {  
         $lastsubonly.='<div class="LC_grade_submissions_body">'.$$string[0].'</div>';   
     } else {  
         $lastsubonly =  
             '<div class="LC_grade_submissions_body">'  
            .'<b>'.&mt('Date Submitted:').'</b> '.$$timestamp."\n";  
   
  my %seenparts;  
  my @part_response_id = &flatten_responseType($responseType);  
  foreach my $part (@part_response_id) {  
     my ($partid,$respid) = @{ $part };  
     my $display_part=&get_display_part($partid,$symb);  
     if ($env{"form.$uname:$udom:$partid:submitted_by"}) {  
  if (exists($seenparts{$partid})) { next; }  
  $seenparts{$partid}=1;  
                 $request->print(  
                     '<b>'.&mt('Part: [_1]',$display_part).'</b>'.  
                     ' <b>'.&mt('Collaborative submission by: [_1]',  
                                '<a href="javascript:viewSubmitter(\''.  
                                $env{"form.$uname:$udom:$partid:submitted_by"}.  
                                '\');" target="_self">'.  
                                $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a>').  
                     '<br />');  
  next;  
     }  
     my $responsetype = $responseType->{$partid}->{$respid};  
     if (!exists($record{"resource.$partid.$respid.submission"})) {  
                 $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.  
                     '<b>'.&mt('Part: [_1]',$display_part).'</b>'.  
                     ' <span class="LC_internal_info">'.  
                     '('.&mt('Response ID: [_1]',$respid).')'.  
                     '</span>&nbsp; &nbsp;'.  
             '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';  
  next;  
     }  
     foreach my $submission (@$string) {  
  my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);  
  if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }  
  my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4);  
  # Similarity check  
                 my $similar='';  
                 my ($type,$trial,$rndseed);  
                 if ($hide eq 'rand') {  
                     $type = 'randomizetry';  
                     $trial = $record{"resource.$partid.tries"};  
                     $rndseed = $record{"resource.$partid.rndseed"};  
                 }  
  if ($env{'form.checkPlag'}) {  
     my ($oname,$odom,$ocrsid,$oessay,$osim)=  
         &most_similar($uname,$udom,$symb,$subval);  
     if ($osim) {  
         $osim=int($osim*100.0);  
                         if ($hide eq 'anon') {  
                             $similar='<hr /><span class="LC_warning">'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'<br />'.  
                                      &mt('As the current submission is for an anonymous survey, no other details are available.').'</span><hr />';  
                         } else {  
     $similar='<hr />';  
                             if ($essayurl eq 'lib/templates/simpleproblem.problem') {  
                                 $similar .= '<h3><span class="LC_warning">'.  
                                             &mt('Essay is [_1]% similar to an essay by [_2]',  
                                                 $osim,  
                                                 &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').  
                                             '</span></h3>';  
                             } elsif ($ocrsid ne '') {  
                                 my %old_course_desc;  
                                 if (ref($coursedesc_by_cid{$ocrsid}) eq 'HASH') {  
                                     %old_course_desc = %{$coursedesc_by_cid{$ocrsid}};  
                                 } else {  
                                     my $args;  
                                     if ($ocrsid ne $env{'request.course.id'}) {  
                                         $args = {'one_time' => 1};  
                                     }  
                                     %old_course_desc =  
                                         &Apache::lonnet::coursedescription($ocrsid,$args);  
                                     $coursedesc_by_cid{$ocrsid} = \%old_course_desc;  
                                 }  
                                 $similar .=  
                                     '<h3><span class="LC_warning">'.  
     &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',  
         $osim,  
         &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',  
         $old_course_desc{'description'},  
         $old_course_desc{'num'},  
         $old_course_desc{'domain'}).  
     '</span></h3>';  
                             } else {  
                                 $similar .=  
                                     '<h3><span class="LC_warning">'.  
                                     &mt('Essay is [_1]% similar to an essay by [_2] in an unknown course',  
                                         $osim,  
                                         &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').  
                                     '</span></h3>';  
                             }  
                             $similar .= '<blockquote><i>'.  
                                         &keywords_highlight($oessay).  
                                         '</i></blockquote><hr />';  
         }  
                     }  
                 }  
  my $order=&get_order($partid,$respid,$symb,$uname,$udom,  
                                      undef,$type,$trial,$rndseed);  
                 if (($env{'form.lastSub'} eq 'lastonly') ||  
                     ($env{'form.lastSub'} eq 'datesub')  ||  
                     ($env{'form.lastSub'} =~ /^(last|all)$/)) {  
     my $display_part=&get_display_part($partid,$symb);  
                     $lastsubonly.='<div class="LC_grade_submission_part">'.  
                         '<b>'.&mt('Part: [_1]',$display_part).'</b>'.  
                         ' <span class="LC_internal_info">'.  
                         '('.&mt('Response ID: [_1]',$respid).')'.  
                         '</span>&nbsp; &nbsp;';  
     my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);  
     if (@$files) {  
                         if ($hide eq 'anon') {  
                             $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));  
                         } else {  
                             $lastsubonly.='<br /><br />'.'<b>'.&mt('Submitted Files:').'</b>'  
                                          .'<br /><span class="LC_warning">';  
                             if(@$files == 1) {  
                                 $lastsubonly .= &mt('Like all files provided by users, this file may contain viruses!');  
                             } else {  
                                 $lastsubonly .= &mt('Like all files provided by users, these files may contain viruses!');  
                             }  
                             $lastsubonly .= '</span>';  
                             foreach my $file (@$files) {  
                                 &Apache::lonnet::allowuploaded('/adm/grades',$file);  
                                 $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" alt="" /> '.$file.'</a>';  
                             }  
                         }  
  $lastsubonly.='<br />';  
     }  
                     if ($hide eq 'anon') {  
                         $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>';   
                     } else {  
                         $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>';  
                         if ($draft) {  
                             $lastsubonly.= ' <span class="LC_warning">'.&mt('Draft Copy').'</span>';  
                         }  
                         $subval =  
     &cleanRecord($subval,$responsetype,$symb,$partid,  
  $respid,\%record,$order,undef,$uname,$udom,$type,$trial,$rndseed);  
                         if ($responsetype eq 'essay') {  
                             $subval =~ s{\n}{<br />}g;  
                         }  
                         $lastsubonly.=$subval."\n";  
                     }  
                     if ($similar) {$lastsubonly.="<br /><br />$similar\n";}  
     $lastsubonly.='</div>';  
  }  
     }  
  }  
  $lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body  
     }  
     $request->print($lastsubonly);      $request->print($lastsubonly);
   
     if ($env{'form.lastSub'} eq 'datesub') {      if ($env{'form.lastSub'} eq 'datesub') {
         my ($parts,$handgrade,$responseType) = &response_type($symb,\$res_error);          my ($parts,$handgrade,$responseType) = &response_type($symb,\$res_error);
  $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));   $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));
     }      }
     if ($env{'form.lastSub'} =~ /^(last|all)$/) {      if ($env{'form.lastSub'} =~ /^(last|all)$/) {
         my $identifier = (&canmodify($usec)? $counter : '');          my $identifier = (&canmodify($usec)? $counter : '');
  $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,          $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
  $env{'request.course.id'},   $env{'request.course.id'},
  $last,'.submission',   $last,'.submission',
  'Apache::grades::keywords_highlight',   'Apache::grades::keywords_highlight',
Line 2696  sub submission { Line 3501  sub submission {
  .$udom.'" />'."\n");   .$udom.'" />'."\n");
     # return if view submission with no grading option      # return if view submission with no grading option
     if (!&canmodify($usec)) {      if (!&canmodify($usec)) {
         $request->print('<p><span class="LC_warning">'.&mt('No grading privileges').'</span></p></div>');   $request->print('<p><span class="LC_warning">'.&mt('No grading privileges').'</span></p></div>');
         return;   return;
     } else {      } else {
  $request->print('</div>'."\n");   $request->print('</div>'."\n");
     }      }
Line 2717  sub submission { Line 3522  sub submission {
         $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript          $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript
         $result.='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".          $result.='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
                  '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".                   '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".
          '&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.                   '&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
                  ',\''.$msgfor.'\');" target="_self">'.                   ',\''.$msgfor.'\');" target="_self">'.
                  &mt('Compose message to student'.(scalar(@$col_fullnames) >= 1 ? 's' : '')).'</a><label> ('.                   &mt('Compose message to student'.(scalar(@$col_fullnames) >= 1 ? 's' : '')).'</a><label> ('.
                  &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.                   &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.
Line 2725  sub submission { Line 3530  sub submission {
                  '/mailbkgrd.gif" width="14" height="10" alt="" name="mailicon'.$counter.'" />'."\n".                   '/mailbkgrd.gif" width="14" height="10" alt="" name="mailicon'.$counter.'" />'."\n".
                  '<br />&nbsp;('.                   '<br />&nbsp;('.
                  &mt('Message will be sent when you click on Save &amp; Next below.').")\n".                   &mt('Message will be sent when you click on Save &amp; Next below.').")\n".
          '</div></div>';                   '</div></div>';
         $request->print($result);          $request->print($result);
     }      }
   
     my %seen = ();      my %seen = ();
     my @partlist;      my @partlist;
     my @gradePartRespid;      my @gradePartRespid;
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id;
       if ($is_tool) {
           @part_response_id = ([0,'']);
       } else {
           @part_response_id = &flatten_responseType($responseType);
       }
     $request->print(      $request->print(
         '<div class="LC_Box">'          '<div class="LC_Box">'
        .'<h3 class="LC_hcell">'.&mt('Assign Grades').'</h3>'         .'<h3 class="LC_hcell">'.&mt('Assign Grades').'</h3>'
Line 2798  sub submission { Line 3608  sub submission {
     return '';      return '';
 }  }
   
   sub show_last_submission {
       my ($uname,$udom,$symb,$essayurl,$responseType,$viewtype,$is_tool,$fullname,
           $record,$coursedesc_by_cid) = @_;
       my ($string,$timestamp,$lastgradetime,$lastsubmittime) =
           &get_last_submission($record,$is_tool);
   
       my ($lastsubonly,$partinfo);
       if ($timestamp eq '') {
           $lastsubonly.='<div class="LC_grade_submissions_body">'.$string->[0].'</div>';
       } elsif ($is_tool) {
           $lastsubonly =
               '<div class="LC_grade_submissions_body">'
              .'<b>'.&mt('Date Grade Passed Back:').'</b> '.$timestamp."</div>\n";
       } else {
           my ($shownsubmdate,$showngradedate);
           if ($lastsubmittime && $lastgradetime) {
               $shownsubmdate = &Apache::lonlocal::locallocaltime($lastsubmittime);
               if ($lastgradetime > $lastsubmittime) {
                    $showngradedate = &Apache::lonlocal::locallocaltime($lastgradetime);
                }
           } else {
               $shownsubmdate = $timestamp;
           }
           $lastsubonly =
               '<div class="LC_grade_submissions_body">'
              .'<b>'.&mt('Date Submitted:').'</b> '.$shownsubmdate."\n";
           if ($showngradedate) {
               $lastsubonly .= '<br /><b>'.&mt('Date Graded:').'</b> '.$showngradedate."\n";
           }
   
           my %seenparts;
           my @part_response_id = &flatten_responseType($responseType);
           foreach my $part (@part_response_id) {
               my ($partid,$respid) = @{ $part };
               my $display_part=&get_display_part($partid,$symb);
               if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
                   if (exists($seenparts{$partid})) { next; }
                   $seenparts{$partid}=1;
                   $partinfo .=
                       '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                       ' <b>'.&mt('Collaborative submission by: [_1]',
                                  '<a href="javascript:viewSubmitter(\''.
                                  $env{"form.$uname:$udom:$partid:submitted_by"}.
                                  '\');" target="_self">'.
                                  $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a>').
                       '<br />';
                   next;
               }
               my $responsetype = $responseType->{$partid}->{$respid};
               if (!exists($record->{"resource.$partid.$respid.submission"})) {
                   $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.
                       '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                       ' <span class="LC_internal_info">'.
                       '('.&mt('Response ID: [_1]',$respid).')'.
                       '</span>&nbsp; &nbsp;'.
                       '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';
                   next;
               }
               foreach my $submission (@$string) {
                   my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
                   if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
                   my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4);
                   # Similarity check
                   my $similar='';
                   my ($type,$trial,$rndseed);
                   if ($hide eq 'rand') {
                       $type = 'randomizetry';
                       $trial = $record->{"resource.$partid.tries"};
                       $rndseed = $record->{"resource.$partid.rndseed"};
                   }
                   if ($env{'form.checkPlag'}) {
                       my ($oname,$odom,$ocrsid,$oessay,$osim)=
                       &most_similar($uname,$udom,$symb,$subval);
                       if ($osim) {
                           $osim=int($osim*100.0);
                           if ($hide eq 'anon') {
                               $similar='<hr /><span class="LC_warning">'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'<br />'.
                                        &mt('As the current submission is for an anonymous survey, no other details are available.').'</span><hr />';
                           } else {
                               $similar='<hr />';
                               if ($essayurl eq 'lib/templates/simpleproblem.problem') {
                                   $similar .= '<h3><span class="LC_warning">'.
                                               &mt('Essay is [_1]% similar to an essay by [_2]',
                                                   $osim,
                                                   &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
                                               '</span></h3>';
                               } else {
                                   my %old_course_desc;
                                   if ($ocrsid ne '') {
                                       if (ref($coursedesc_by_cid->{$ocrsid}) eq 'HASH') {
                                           %old_course_desc = %{$coursedesc_by_cid->{$ocrsid}};
                                       } else {
                                           my $args;
                                           if ($ocrsid ne $env{'request.course.id'}) {
                                               $args = {'one_time' => 1};
                                           }
                                           %old_course_desc =
                                               &Apache::lonnet::coursedescription($ocrsid,$args);
                                           $coursedesc_by_cid->{$ocrsid} = \%old_course_desc;
                                       }
                                       $similar .=
                                           '<h3><span class="LC_warning">'.
                                           &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',
                                               $osim,
                                               &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',
                                               $old_course_desc{'description'},
                                               $old_course_desc{'num'},
                                               $old_course_desc{'domain'}).
                                           '</span></h3>';
                                   } else {
                                       $similar .=
                                           '<h3><span class="LC_warning">'.
                                           &mt('Essay is [_1]% similar to an essay by [_2] in an unknown course',
                                               $osim,
                                               &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
                                           '</span></h3>';
                                   }
                               }
                               $similar .= '<blockquote><i>'.
                                           &keywords_highlight($oessay).
                                           '</i></blockquote><hr />';
                           }
                       }
                   }
                   my $order=&get_order($partid,$respid,$symb,$uname,$udom,
                                        undef,$type,$trial,$rndseed);
                   if (($viewtype eq 'lastonly') ||
                       ($viewtype eq 'datesub')  ||
                       ($viewtype =~ /^(last|all)$/)) {
                       my $display_part=&get_display_part($partid,$symb);
                       $lastsubonly.='<div class="LC_grade_submission_part">'.
                           '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                           ' <span class="LC_internal_info">'.
                           '('.&mt('Response ID: [_1]',$respid).')'.
                           '</span>&nbsp; &nbsp;';
                       my $files=&get_submitted_files($udom,$uname,$partid,$respid,$record);
                       if (@$files) {
                           if ($hide eq 'anon') {
                               $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));
                           } else {
                               $lastsubonly.='<br /><br />'.'<b>'.&mt('Submitted Files:').'</b>'
                                           .'<br /><span class="LC_warning">';
                               if(@$files == 1) {
                                   $lastsubonly .= &mt('Like all files provided by users, this file may contain viruses!');
                               } else {
                                   $lastsubonly .= &mt('Like all files provided by users, these files may contain viruses!');
                               }
                               $lastsubonly .= '</span>';
                               foreach my $file (@$files) {
                                   &Apache::lonnet::allowuploaded('/adm/grades',$file);
                                   $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" alt="" /> '.$file.'</a>';
                               }
                           }
                           $lastsubonly.='<br />';
                       }
                       if ($hide eq 'anon') {
                           $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>';
                       } else {
                           $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>';
                           if ($draft) {
                               $lastsubonly.= ' <span class="LC_warning">'.&mt('Draft Copy').'</span>';
                           }
                           $subval =
                               &cleanRecord($subval,$responsetype,$symb,$partid,
                                            $respid,$record,$order,undef,$uname,$udom,$type,$trial,$rndseed);
                           if ($responsetype eq 'essay') {
                               $subval =~ s{\n}{<br />}g;
                           }
                           $lastsubonly.=$subval."\n";
                       }
                       if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
                       $lastsubonly.='</div>';
                   }
               }
           }
           $lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body
       }
       return ($lastsubonly,$partinfo);
   }
   
 sub check_collaborators {  sub check_collaborators {
     my ($symb,$uname,$udom,$record,$handgrade,$counter) = @_;      my ($symb,$uname,$udom,$record,$handgrade,$counter) = @_;
     my ($result,@col_fullnames);      my ($result,@col_fullnames);
Line 2856  sub check_collaborators { Line 3846  sub check_collaborators {
   
 #--- Retrieve the last submission for all the parts  #--- Retrieve the last submission for all the parts
 sub get_last_submission {  sub get_last_submission {
     my ($returnhash)=@_;      my ($returnhash,$is_tool)=@_;
     my (@string,$timestamp,%lasthidden);      my (@string,$timestamp,$lastgradetime,$lastsubmittime);
     if ($$returnhash{'version'}) {      if ($$returnhash{'version'}) {
  my %lasthash=();   my %lasthash=();
  my ($version);          my %prevsolved=();
           my %solved=();
    my $version;
  for ($version=1;$version<=$$returnhash{'version'};$version++) {   for ($version=1;$version<=$$returnhash{'version'};$version++) {
               my %handgraded = ();
     foreach my $key (sort(split(/\:/,      foreach my $key (sort(split(/\:/,
  $$returnhash{$version.':keys'}))) {   $$returnhash{$version.':keys'}))) {
  $lasthash{$key}=$$returnhash{$version.':'.$key};   $lasthash{$key}=$$returnhash{$version.':'.$key};
  $timestamp =                   if ($key =~ /\.([^.]+)\.regrader$/) {
     &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});                      $handgraded{$1} = 1;
                   } elsif ($key =~ /\.portfiles$/) {
                       if (($$returnhash{$version.':'.$key} ne '') &&
                           ($$returnhash{$version.':'.$key} !~ /\.\d+\.\w+$/)) {
                           $lastsubmittime = $$returnhash{$version.':timestamp'};
                       }
                   } elsif ($key =~ /\.submission$/) {
                       if ($$returnhash{$version.':'.$key} ne '') {
                           $lastsubmittime = $$returnhash{$version.':timestamp'};
                       }
                   } elsif ($key =~ /\.([^.]+)\.solved$/) {
                       $prevsolved{$1} = $solved{$1};
                       $solved{$1} = $lasthash{$key};
                   }
               }
               foreach my $partid (keys(%handgraded)) {
                   if (($prevsolved{$partid} eq 'ungraded_attempted') &&
                       (($solved{$partid} eq 'incorrect_by_override') ||
                        ($solved{$partid} eq 'correct_by_override'))) {
                       $lastgradetime = $$returnhash{$version.':timestamp'};
                   }
                   if ($solved{$partid} ne '') {
                       $prevsolved{$partid} = $solved{$partid};
                   }
     }      }
  }   }
   #
   # Timestamp is for last transaction for this resource, which does not
   # necessarily correspond to the time of last submission for problem (or part).
   #
           if ($lasthash{'timestamp'} ne '') {
               $timestamp = &Apache::lonlocal::locallocaltime($lasthash{'timestamp'});
           }
         my (%typeparts,%randombytry);          my (%typeparts,%randombytry);
         my $showsurv =           my $showsurv = 
             &Apache::lonnet::allowed('vas',$env{'request.course.id'});              &Apache::lonnet::allowed('vas',$env{'request.course.id'});
Line 2922  sub get_last_submission { Line 3945  sub get_last_submission {
  }   }
     }      }
     if (!@string) {      if (!@string) {
           my $msg;
           if ($is_tool) {
               $msg = &mt('No grade passed back.');
           } else {
               $msg = &mt('Nothing submitted - no attempts.');
           }
  $string[0] =   $string[0] =
     '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span>';      '<span class="LC_warning">'.$msg.'</span>';
     }      }
     return (\@string,\$timestamp);      return (\@string,$timestamp,$lastgradetime,$lastsubmittime);
 }  }
   
 #--- High light keywords, with style choosen by user.  #--- High light keywords, with style choosen by user.
Line 2955  sub show_previous_task_version { Line 3984  sub show_previous_task_version {
     my ($uname,$udom) = ($env{'form.student'},$env{'form.userdom'});      my ($uname,$udom) = ($env{'form.student'},$env{'form.userdom'});
     my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});      my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});
     if (!&canview($usec)) {      if (!&canview($usec)) {
         $request->print('<span class="LC_warning">'.          $request->print(
                         &mt('Unable to view previous version for requested student.').              '<span class="LC_warning">'.
                         ' '.&mt('([_1] in section [_2] in course id [_3])',              &mt('Unable to view previous version for requested student.').
                                 $uname.':'.$udom,$usec,$env{'request.course.id'}).              ' '.&mt('([_1] in section [_2] in course id [_3])',
                         '</span>');                      $uname.':'.$udom,$usec,$env{'request.course.id'}).
               '</span>');
         return;          return;
     }      }
     my $mode = 'both';      my $mode = 'both';
Line 3131  sub processHandGrade { Line 4161  sub processHandGrade {
     my $ntstu  = $env{'form.NTSTU'};      my $ntstu  = $env{'form.NTSTU'};
     my $cdom   = $env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom   = $env{'course.'.$env{'request.course.id'}.'.domain'};
     my $cnum   = $env{'course.'.$env{'request.course.id'}.'.num'};      my $cnum   = $env{'course.'.$env{'request.course.id'}.'.num'};
       my ($res_error,%queueable);
       my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print(&navmap_errormsg());
           return;
       } else {
           foreach my $part (@{$partlist}) {
               if (ref($responseType->{$part}) eq 'HASH') {
                   foreach my $id (keys(%{$responseType->{$part}})) {
                       if (($responseType->{$part}->{$id} eq 'essay') ||
                           (lc($handgrade->{$part.'_'.$id}) eq 'yes')) {
                           $queueable{$part} = 1;
                           last;
                       }
                   }
               }
           }
       }
   
     if ($button eq 'Save & Next') {      if ($button eq 'Save & Next') {
           my %needpb = &passbacks_for_symb($cdom,$cnum,$symb);
           my (%skip_passback,%pbsave,%pbcollab);
  my $ctr = 0;   my $ctr = 0;
  while ($ctr < $ngrade) {   while ($ctr < $ngrade) {
     my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr});      my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr});
     my ($errorflag,$pts,$wgt,$numhidden) =       my ($errorflag,$pts,$wgt,$numhidden) = 
                 &saveHandGrade($request,$symb,$uname,$udom,$ctr);                  &saveHandGrade($request,$symb,$uname,$udom,$ctr,undef,undef,\%queueable,\%needpb,\%skip_passback,\%pbsave);
     if ($errorflag eq 'no_score') {      if ($errorflag eq 'no_score') {
  $ctr++;   $ctr++;
  next;   next;
     }      }
     if ($errorflag eq 'not_allowed') {      if ($errorflag eq 'not_allowed') {
                 $request->print(   $request->print(
                     '<span class="LC_error">'                      '<span class="LC_error">'
                    .&mt('Not allowed to modify grades for [_1]',"$uname:$udom")                     .&mt('Not allowed to modify grades for [_1]',"$uname:$udom")
                    .'</span>');                     .'</span>');
Line 3192  sub processHandGrade { Line 4242  sub processHandGrade {
     foreach my $collaborator (@collaborators) {      foreach my $collaborator (@collaborators) {
  my ($errorflag,$pts,$wgt) =    my ($errorflag,$pts,$wgt) = 
     &saveHandGrade($request,$symb,$collaborator,$udom,$ctr,      &saveHandGrade($request,$symb,$collaborator,$udom,$ctr,
    $env{'form.unamedom'.$ctr},$part);     $env{'form.unamedom'.$ctr},$part,\%queueable,\%needpb,\%skip_passback,%pbsave);
  if ($errorflag eq 'not_allowed') {   if ($errorflag eq 'not_allowed') {
     $request->print("<span class=\"LC_error\">".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom")."</span>");      $request->print("<span class=\"LC_error\">".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom")."</span>");
     next;      next;
  } elsif ($message ne '') {   } else {
     my ($baseurl,$showsymb) =                               $pbcollab{$collaborator}{$part} = [$pts,$wgt];
  &get_feedurl_and_symb($symb,$collaborator,                              if ($message ne '') {
       $udom);          my ($baseurl,$showsymb) = 
     if ($env{'form.withgrades'.$ctr}) {      &get_feedurl_and_symb($symb,$collaborator,
  $messagetail = " for <a href=\"".            $udom);
                                     $baseurl."?symb=$showsymb\">$restitle</a>";          if ($env{'form.withgrades'.$ctr}) {
       $messagetail = " for <a href=\"".
                                           $baseurl."?symb=$showsymb\">$restitle</a>";
           }
           $msgstatus =
       &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle);
     }      }
     $msgstatus =           }
  &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle);  
  }  
     }      }
  }   }
     }      }
     $ctr++;      $ctr++;
  }   }
     }          if ((keys(%pbcollab)) && (keys(%needpb))) {
               # FIXME passback scores for collaborators
     my $res_error;          }
     my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,\$res_error);  
     if ($res_error) {  
         $request->print(&navmap_errormsg());  
         return;  
     }      }
   
     my %keyhash = ();      my %keyhash = ();
Line 3251  sub processHandGrade { Line 4300  sub processHandGrade {
  $ctr = 0;   $ctr = 0;
  while ($ctr < $ngrade) {   while ($ctr < $ngrade) {
     if ($env{'form.newmsg'.$ctr} ne '') {      if ($env{'form.newmsg'.$ctr} ne '') {
  $keyhash{$symb.'_savemsg'.$idx} = $env{'form.newmsg'.$ctr};          $keyhash{$symb.'_savemsg'.$idx} = $env{'form.newmsg'.$ctr};
  $env{'form.savemsg'.$idx} = $env{'form.newmsg'.$ctr};          $env{'form.savemsg'.$idx} = $env{'form.newmsg'.$ctr};
  $idx++;          $idx++;
     }      }
     $ctr++;      $ctr++;
  }   }
Line 3261  sub processHandGrade { Line 4310  sub processHandGrade {
  $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};   $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};
     }      }
     if (($numessay) || ($env{'form.compmsg'})) {      if (($numessay) || ($env{'form.compmsg'})) {
  my $putresult = &Apache::lonnet::put          my $putresult = &Apache::lonnet::put
     ('nohist_handgrade',\%keyhash,$cdom,$cnum);              ('nohist_handgrade',\%keyhash,$cdom,$cnum);
     }      }
   
     # Called by Save & Refresh from Highlight Attribute Window      # Called by Save & Refresh from Highlight Attribute Window
Line 3364  sub processHandGrade { Line 4413  sub processHandGrade {
  $ctr++;   $ctr++;
     }      }
     if ($total < 0) {      if ($total < 0) {
         my $the_end.='<p>'.&mt('[_1]Message:[_2] No more students for this section or class.','<b>','</b>').'</p>'."\n";   my $the_end.='<p>'.&mt('[_1]Message:[_2] No more students for this section or class.','<b>','</b>').'</p>'."\n";
  $request->print($the_end);   $request->print($the_end);
     }      }
     return '';      return '';
Line 3372  sub processHandGrade { Line 4421  sub processHandGrade {
   
 #---- Save the score and award for each student, if changed  #---- Save the score and award for each student, if changed
 sub saveHandGrade {  sub saveHandGrade {
     my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_;      my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part,$queueable,$needpb,$skip_passback,$pbsave) = @_;
     my @version_parts;      my @version_parts;
     my $usec = &Apache::lonnet::getsection($domain,$stuname,      my $usec = &Apache::lonnet::getsection($domain,$stuname,
    $env{'request.course.id'});     $env{'request.course.id'});
Line 3383  sub saveHandGrade { Line 4432  sub saveHandGrade {
     my ($pts,$wgt,$totchg) = ('','',0);      my ($pts,$wgt,$totchg) = ('','',0);
     my %aggregate = ();      my %aggregate = ();
     my $aggregateflag = 0;      my $aggregateflag = 0;
       my $sendupdate;
     if ($env{'form.HIDE'.$newflg}) {      if ($env{'form.HIDE'.$newflg}) {
         my ($version,$parts) = split(/:/,$env{'form.HIDE'.$newflg},2);          my ($version,$parts) = split(/:/,$env{'form.HIDE'.$newflg},2);
         my $numchgs = &makehidden($version,$parts,\%record,$symb,$domain,$stuname,1);          my $numchgs = &makehidden($version,$parts,\%record,$symb,$domain,$stuname,1);
         $totchg += $numchgs;          $totchg += $numchgs;
           if ($numchgs) {
               $sendupdate = 1;
           }
     }      }
       my (%weights,%awardeds,%excuseds);
     my @parts = split(/:/,$env{'form.partlist'.$newflg});      my @parts = split(/:/,$env{'form.partlist'.$newflg});
     foreach my $new_part (@parts) {      foreach my $new_part (@parts) {
  #collaborator ($submi may vary for different parts   #collaborator ($submi may vary for different parts
  if ($submitter && $new_part ne $part) { next; }   if ($submitter && $new_part ne $part) { next; }
  my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$new_part};   my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$new_part};
           if ($env{'form.WGT'.$newflg.'_'.$new_part} eq '') {
               $weights{$symb}{$new_part} = 1;
           } else {
               $weights{$symb}{$new_part} = $env{'form.WGT'.$newflg.'_'.$new_part};
           }
  if ($dropMenu eq 'excused') {   if ($dropMenu eq 'excused') {
               $excuseds{$symb}{$new_part} = 1;
               $awardeds{$symb}{$new_part} = '';
     if ($record{'resource.'.$new_part.'.solved'} ne 'excused') {      if ($record{'resource.'.$new_part.'.solved'} ne 'excused') {
  $newrecord{'resource.'.$new_part.'.solved'} = 'excused';   $newrecord{'resource.'.$new_part.'.solved'} = 'excused';
  if (exists($record{'resource.'.$new_part.'.awarded'})) {   if (exists($record{'resource.'.$new_part.'.awarded'})) {
     $newrecord{'resource.'.$new_part.'.awarded'} = '';      $newrecord{'resource.'.$new_part.'.awarded'} = '';
  }   }
         $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";          $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
                   $sendupdate = 1;
     }      }
  } elsif ($dropMenu eq 'reset status'   } elsif ($dropMenu eq 'reset status'
  && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts   && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts
Line 3423  sub saveHandGrade { Line 4485  sub saveHandGrade {
                 &decrement_aggs($symb,$new_part,\%aggregate,$aggtries,$totaltries,$solvedstatus);                  &decrement_aggs($symb,$new_part,\%aggregate,$aggtries,$totaltries,$solvedstatus);
                 $aggregateflag = 1;                  $aggregateflag = 1;
             }              }
               $sendupdate = 1;
               $excuseds{$symb}{$new_part} = '';
               $awardeds{$symb}{$new_part} = '';
  } elsif ($dropMenu eq '') {   } elsif ($dropMenu eq '') {
     $pts = ($env{'form.GD_BOX'.$newflg.'_'.$new_part} ne '' ?       $pts = ($env{'form.GD_BOX'.$newflg.'_'.$new_part} ne '' ? 
     $env{'form.GD_BOX'.$newflg.'_'.$new_part} :       $env{'form.GD_BOX'.$newflg.'_'.$new_part} : 
Line 3433  sub saveHandGrade { Line 4498  sub saveHandGrade {
     $wgt = $env{'form.WGT'.$newflg.'_'.$new_part} eq '' ? 1 :       $wgt = $env{'form.WGT'.$newflg.'_'.$new_part} eq '' ? 1 : 
  $env{'form.WGT'.$newflg.'_'.$new_part};   $env{'form.WGT'.$newflg.'_'.$new_part};
     my $partial= $pts/$wgt;      my $partial= $pts/$wgt;
               $awardeds{$symb}{$new_part} = $partial;
               $excuseds{$symb}{$new_part} = '';
     if ($partial eq $record{'resource.'.$new_part.'.awarded'}) {      if ($partial eq $record{'resource.'.$new_part.'.awarded'}) {
  #do not update score for part if not changed.   #do not update score for part if not changed.
                 &handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord);                  &handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord);
  next;   next;
     } else {      } else {
         push(@parts_graded,$new_part);          push(@parts_graded,$new_part);
                   $sendupdate = 1;
     }      }
     if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {      if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {
  $newrecord{'resource.'.$new_part.'.awarded'}  = $partial;   $newrecord{'resource.'.$new_part.'.awarded'}  = $partial;
Line 3484  sub saveHandGrade { Line 4552  sub saveHandGrade {
  &Apache::lonnet::cstore(\%newrecord,$symb,   &Apache::lonnet::cstore(\%newrecord,$symb,
  $env{'request.course.id'},$domain,$stuname);   $env{'request.course.id'},$domain,$stuname);
  &check_and_remove_from_queue(\@parts,\%record,\%newrecord,$symb,   &check_and_remove_from_queue(\@parts,\%record,\%newrecord,$symb,
      $cdom,$cnum,$domain,$stuname);       $cdom,$cnum,$domain,$stuname,$queueable);
     }      }
     if ($aggregateflag) {      if ($aggregateflag) {
         &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,          &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
       $cdom,$cnum);        $cdom,$cnum);
     }      }
       if (($sendupdate) && (!$submitter)) {
           if ((ref($needpb) eq 'HASH') &&
               (keys(%{$needpb}))) {
               &process_passbacks('handgrade',[$symb],$cdom,$cnum,$domain,$stuname,\%weights,
                                  \%awardeds,\%excuseds,$needpb,$skip_passback,$pbsave);
           }
       }
     return ('',$pts,$wgt,$totchg);      return ('',$pts,$wgt,$totchg);
 }  }
   
Line 3524  sub makehidden { Line 4599  sub makehidden {
 }  }
   
 sub check_and_remove_from_queue {  sub check_and_remove_from_queue {
     my ($parts,$record,$newrecord,$symb,$cdom,$cnum,$domain,$stuname) = @_;      my ($parts,$record,$newrecord,$symb,$cdom,$cnum,$domain,$stuname,$queueable) = @_;
     my @ungraded_parts;      my @ungraded_parts;
     foreach my $part (@{$parts}) {      foreach my $part (@{$parts}) {
  if (    $record->{   'resource.'.$part.'.awarded'} eq ''   if (    $record->{   'resource.'.$part.'.awarded'} eq ''
Line 3532  sub check_and_remove_from_queue { Line 4607  sub check_and_remove_from_queue {
      && $newrecord->{'resource.'.$part.'.awarded'} eq ''       && $newrecord->{'resource.'.$part.'.awarded'} eq ''
      && $newrecord->{'resource.'.$part.'.solved' } ne 'excused'       && $newrecord->{'resource.'.$part.'.solved' } ne 'excused'
  ) {   ) {
     push(@ungraded_parts, $part);              if ($queueable->{$part}) {
           push(@ungraded_parts, $part);
               }
  }   }
     }      }
     if ( !@ungraded_parts ) {      if ( !@ungraded_parts ) {
Line 3558  sub handback_files { Line 4635  sub handback_files {
  my $part_resp = join('_',@{ $part_response_id });   my $part_resp = join('_',@{ $part_response_id });
         if (($env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'} =~ /^\d+$/) & ($new_part eq $part_id)) {          if (($env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'} =~ /^\d+$/) & ($new_part eq $part_id)) {
             for (my $counter=1; $counter<=$env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'}; $counter++) {              for (my $counter=1; $counter<=$env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'}; $counter++) {
                 # if multiple files are uploaded names will be 'returndoc2','returndoc3'                  # if multiple files are uploaded names will be 'returndoc2','returndoc3' 
  if ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter}) {                  if ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter}) {
                     my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter.'.filename'};                      my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter.'.filename'};
                     my ($directory,$answer_file) =                       my ($directory,$answer_file) = 
                         ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter} =~ /^(.*?)([^\/]*)$/);                          ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter} =~ /^(.*?)([^\/]*)$/);
                     my ($answer_name,$answer_ver,$answer_ext) =                      my ($answer_name,$answer_ver,$answer_ext) =
         &file_name_version_ext($answer_file);          &Apache::lonnet::file_name_version_ext($answer_file);
     my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);      my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);
                     my $getpropath = 1;                      my $getpropath = 1;
                     my ($dir_list,$listerror) =                      my ($dir_list,$listerror) =
                         &Apache::lonnet::dirlist($portfolio_root.$portfolio_path,                          &Apache::lonnet::dirlist($portfolio_root.$portfolio_path,
                                                  $domain,$stuname,$getpropath);                                                   $domain,$stuname,$getpropath);
     my $version = &get_next_version($answer_name,$answer_ext,$dir_list);      my $version = &Apache::lonnet::get_next_version($answer_name,$answer_ext,$dir_list);
                     # fix filename                      # fix filename
                     my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);                      my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);
                     my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain,                      my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain,
Line 3588  sub handback_files { Line 4665  sub handback_files {
     $$newrecord{"resource.$new_part.$resp_id.handback"}.=',';      $$newrecord{"resource.$new_part.$resp_id.handback"}.=',';
  }   }
                         $$newrecord{"resource.$new_part.$resp_id.handback"} .= $save_file_name;                          $$newrecord{"resource.$new_part.$resp_id.handback"} .= $save_file_name;
  $file_msg.='<span class="LC_filename"><a href="/uploaded/'."$domain/$stuname/".$save_file_name.'">'.$save_file_name."</a></span> <br />";   $file_msg.= '<span class="LC_filename"><a href="/uploaded/'."$domain/$stuname/".$save_file_name.'">'.$save_file_name."</a></span> <br />";
   
                     }                      }
                     $request->print('<br />'.&mt('[_1] will be the uploaded filename [_2]','<span class="LC_info">'.$fname.'</span>','<span class="LC_filename">'.$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter}.'</span>'));                      $request->print('<br />'.&mt('[_1] will be the uploaded filename [_2]','<span class="LC_info">'.$fname.'</span>','<span class="LC_filename">'.$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter}.'</span>'));
                 }                  }
Line 3600  sub handback_files { Line 4676  sub handback_files {
         $request->print('<br />');          $request->print('<br />');
         my @what = ($symb,$env{'request.course.id'},'handback');          my @what = ($symb,$env{'request.course.id'},'handback');
         &Apache::lonnet::mark_as_readonly($domain,$stuname,\@handedback,\@what);          &Apache::lonnet::mark_as_readonly($domain,$stuname,\@handedback,\@what);
         my $user_lh = &Apache::loncommon::user_lang($stuname,$domain,$env{'request.course.id'});          my $user_lh = &Apache::loncommon::user_lang($stuname,$domain,$env{'request.course.id'});    
         my ($subject,$message);          my ($subject,$message);
         if (scalar(@handedback) == 1) {          if (scalar(@handedback) == 1) {
             $subject = &mt_user($user_lh,'File Handed Back by Instructor');              $subject = &mt_user($user_lh,'File Handed Back by Instructor');
Line 3720  sub version_portfiles { Line 4796  sub version_portfiles {
     my $version_parts = join('|',@$v_flag);      my $version_parts = join('|',@$v_flag);
     my @returned_keys;      my @returned_keys;
     my $parts = join('|', @$parts_graded);      my $parts = join('|', @$parts_graded);
     my $portfolio_root = '/userfiles/portfolio';  
     foreach my $key (keys(%$record)) {      foreach my $key (keys(%$record)) {
         my $new_portfiles;          my $new_portfiles;
         if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {          if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {
             my @versioned_portfiles;              my @versioned_portfiles;
             my @portfiles = split(/\s*,\s*/,$$record{$key});              my @portfiles = split(/\s*,\s*/,$$record{$key});
             foreach my $file (@portfiles) {              if (@portfiles) {
                 &Apache::lonnet::unmark_as_readonly($domain,$stu_name,[$symb,$env{'request.course.id'}],$file);                  &Apache::lonnet::portfiles_versioning($symb,$domain,$stu_name,\@portfiles,
                 my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);                                                        \@versioned_portfiles);
  my ($answer_name,$answer_ver,$answer_ext) =  
     &file_name_version_ext($answer_file);  
                 my $getpropath = 1;  
                 my ($dir_list,$listerror) =  
                     &Apache::lonnet::dirlist($portfolio_root.$directory,$domain,  
                                              $stu_name,$getpropath);  
                 my $version = &get_next_version($answer_name,$answer_ext,$dir_list);  
                 my $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version);  
                 if ($new_answer ne 'problem getting file') {  
                     push(@versioned_portfiles, $directory.$new_answer);  
                     &Apache::lonnet::mark_as_readonly($domain,$stu_name,  
                         [$directory.$new_answer],  
                         [$symb,$env{'request.course.id'},'graded']);  
                 }  
             }              }
             $$record{$key} = join(',',@versioned_portfiles);              $$record{$key} = join(',',@versioned_portfiles);
             push(@returned_keys,$key);              push(@returned_keys,$key);
         }          }
     }   
     return (@returned_keys);     
 }  
   
 sub get_next_version {  
     my ($answer_name, $answer_ext, $dir_list) = @_;  
     my $version;  
     if (ref($dir_list) eq 'ARRAY') {  
         foreach my $row (@{$dir_list}) {  
             my ($file) = split(/\&/,$row,2);  
             my ($file_name,$file_version,$file_ext) =  
         &file_name_version_ext($file);  
             if (($file_name eq $answer_name) &&   
         ($file_ext eq $answer_ext)) {  
                 # gets here if filename and extension match,   
                 # regardless of version  
                 if ($file_version ne '') {  
                     # a versioned file is found  so save it for later  
                     if ($file_version > $version) {  
         $version = $file_version;  
                     }  
         }  
             }  
         }  
     }      }
     $version ++;      return (@returned_keys);
     return($version);  
 }  
   
 sub version_selected_portfile {  
     my ($domain,$stu_name,$directory,$file_name,$version) = @_;  
     my ($answer_name,$answer_ver,$answer_ext) =  
         &file_name_version_ext($file_name);  
     my $new_answer;  
     $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stu_name/portfolio$directory$file_name");  
     if($env{'form.copy'} eq '-1') {  
         $new_answer = 'problem getting file';  
     } else {  
         $new_answer = $answer_name.'.'.$version.'.'.$answer_ext;  
         my $copy_result = &Apache::lonnet::finishuserfileupload(  
                             $stu_name,$domain,'copy',  
         '/portfolio'.$directory.$new_answer);  
     }      
     return ($new_answer);  
 }  
   
 sub file_name_version_ext {  
     my ($file)=@_;  
     my @file_parts = split(/\./, $file);  
     my ($name,$version,$ext);  
     if (@file_parts > 1) {  
  $ext=pop(@file_parts);  
  if (@file_parts > 1 && $file_parts[-1] =~ /^\d+$/) {  
     $version=pop(@file_parts);  
  }  
  $name=join('.',@file_parts);  
     } else {  
  $name=join('.',@file_parts);  
     }  
     return($name,$version,$ext);  
 }  }
   
 #--------------------------------------------------------------------------------------  #--------------------------------------------------------------------------------------
Line 3986  VIEWJAVASCRIPT Line 4989  VIEWJAVASCRIPT
 #--- show scores for a section or whole class w/ option to change/update a score  #--- show scores for a section or whole class w/ option to change/update a score
 sub viewgrades {  sub viewgrades {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
       my ($is_tool,$toolsymb);
       if ($symb =~ /ext\.tool$/) {
           $is_tool = 1;
           $toolsymb = $symb;
       }
     &viewgrades_js($request);      &viewgrades_js($request);
   
     #need to make sure we have the correct data for later EXT calls,       #need to make sure we have the correct data for later EXT calls, 
Line 4049  sub viewgrades { Line 5057  sub viewgrades {
             $common_header = &mt('Assign Common Grade to Students not assigned to any groups');              $common_header = &mt('Assign Common Grade to Students not assigned to any groups');
             $specific_header = &mt('Assign Grade to Specific Students not assigned to any groups');              $specific_header = &mt('Assign Grade to Specific Students not assigned to any groups');
         } else {          } else {
             $common_header = &mt('Assign Common Grade to Class');      $common_header = &mt('Assign Common Grade to Class');
             $specific_header = &mt('Assign Grade to Specific Students in Class');              $specific_header = &mt('Assign Grade to Specific Students in Class');
         }          }
     } elsif (grep(/^none$/,@sections)) {      } elsif (grep(/^none$/,@sections)) {
Line 4062  sub viewgrades { Line 5070  sub viewgrades {
             $specific_header = &mt('Assign Grade to Specific Students in no Section and in no Group');              $specific_header = &mt('Assign Grade to Specific Students in no Section and in no Group');
         } else {          } else {
             $common_header = &mt('Assign Common Grade to Students in no Section');              $common_header = &mt('Assign Common Grade to Students in no Section');
             $specific_header = &mt('Assign Grade to Specific Students in no Section');      $specific_header = &mt('Assign Grade to Specific Students in no Section');
         }          }
     } else {      } else {
         $section_display = join (", ",@sections);          $section_display = join (", ",@sections);
Line 4076  sub viewgrades { Line 5084  sub viewgrades {
             $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1] and no Group',$section_display);              $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1] and no Group',$section_display);
         } else {          } else {
             $common_header = &mt('Assign Common Grade to Students in Section(s) [_1]',$section_display);              $common_header = &mt('Assign Common Grade to Students in Section(s) [_1]',$section_display);
             $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1]',$section_display);      $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1]',$section_display);
         }          }
     }      }
     my %submit_types = &substatus_options();      my %submit_types = &substatus_options();
Line 4085  sub viewgrades { Line 5093  sub viewgrades {
     if ($env{'form.submitonly'} eq 'all') {      if ($env{'form.submitonly'} eq 'all') {
         $result.= '<h3>'.$common_header.'</h3>';          $result.= '<h3>'.$common_header.'</h3>';
     } else {      } else {
         $result.= '<h3>'.$common_header.'&nbsp;'.&mt('(submission status: "[_1]")',$submission_status).'</h3>';           my $text;
           if ($is_tool) {
               $text = &mt('(transaction status: "[_1]")',$submission_status);
           } else {
               $text = &mt('(submission status: "[_1]")',$submission_status);
           }
           $result.= '<h3>'.$common_header.'&nbsp;'.$text.'</h3>';
     }      }
     $result .= &Apache::loncommon::start_data_table();      $result .= &Apache::loncommon::start_data_table();
     #radio buttons/text box for assigning points for a section or class.      #radio buttons/text box for assigning points for a section or class.
Line 4098  sub viewgrades { Line 5112  sub viewgrades {
     my %weight = ();      my %weight = ();
     my $ctsparts = 0;      my $ctsparts = 0;
     my %seen = ();      my %seen = ();
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id;
       if ($is_tool) {
           @part_response_id = ([0,'']);
       } else {
           @part_response_id = &flatten_responseType($responseType);
       }
     foreach my $part_response_id (@part_response_id) {      foreach my $part_response_id (@part_response_id) {
     my ($partid,$respid) = @{ $part_response_id };      my ($partid,$respid) = @{ $part_response_id };
  my $part_resp = join('_',@{ $part_response_id });   my $part_resp = join('_',@{ $part_response_id });
Line 4122  sub viewgrades { Line 5141  sub viewgrades {
     $partid.'" size="4" '.'onchange="javascript:writePoint(\''.      $partid.'" size="4" '.'onchange="javascript:writePoint(\''.
  $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.   $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.
     $weight{$partid}.' '.&mt('(problem weight)').'</td>'."\n";      $weight{$partid}.' '.&mt('(problem weight)').'</td>'."\n";
  $line.= '<td><b>'.&mt('Grade Status').':</b>'.          $line.= '<td><b>'.&mt('Grade Status').':</b>'.
                 '<select name="SELVAL_'.$partid.'" '.              '<select name="SELVAL_'.$partid.'" '.
         'onchange="javascript:writeRadText(\''.$partid.'\','.              'onchange="javascript:writeRadText(\''.$partid.'\','.
  $weight{$partid}.')"> '.                  $weight{$partid}.')"> '.
     '<option selected="selected"> </option>'.      '<option selected="selected"> </option>'.
     '<option value="excused">'.&mt('excused').'</option>'.      '<option value="excused">'.&mt('excused').'</option>'.
     '<option value="reset status">'.&mt('reset status').'</option>'.      '<option value="reset status">'.&mt('reset status').'</option>'.
Line 4149  sub viewgrades { Line 5168  sub viewgrades {
   
     #table listing all the students in a section/class      #table listing all the students in a section/class
     #header of table      #header of table
     if ($env{'form.submitonly'} eq 'all') {       if ($env{'form.submitonly'} eq 'all') {
         $result.= '<h3>'.$specific_header.'</h3>';          $result.= '<h3>'.$specific_header.'</h3>';
     } else {      } else {
         $result.= '<h3>'.$specific_header.'&nbsp;'.&mt('(submission status: "[_1]")',$submission_status).'</h3>';          my $text;
           if ($is_tool) {
               $text = &mt('(transaction status: "[_1]")',$submission_status);
           } else {
               $text = &mt('(submission status: "[_1]")',$submission_status);
           }
           $result.= '<h3>'.$specific_header.'&nbsp;'.$text.'</h3>';
     }      }
     $result.= &Apache::loncommon::start_data_table().      $result.= &Apache::loncommon::start_data_table().
       &Apache::loncommon::start_data_table_header_row().        &Apache::loncommon::start_data_table_header_row().
Line 4166  sub viewgrades { Line 5191  sub viewgrades {
     my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);      my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
     my @partids = ();      my @partids = ();
     foreach my $part (@parts) {      foreach my $part (@parts) {
  my $display=&Apache::lonnet::metadata($url,$part.'.display');   my $display=&Apache::lonnet::metadata($url,$part.'.display',$toolsymb);
         my $narrowtext = &mt('Tries');          my $narrowtext = &mt('Tries');
  $display =~ s|^Number of Attempts|$narrowtext <br />|; # makes the column narrower   $display =~ s|^Number of Attempts|$narrowtext <br />|; # makes the column narrower
  if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }   if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name',$toolsymb); }
  my ($partid) = &split_part_type($part);   my ($partid) = &split_part_type($part);
         push(@partids,$partid);          push(@partids,$partid);
 #  #
Line 4178  sub viewgrades { Line 5203  sub viewgrades {
  my $display_part=&get_display_part($partid,$symb);   my $display_part=&get_display_part($partid,$symb);
  if ($display =~ /^Partial Credit Factor/) {   if ($display =~ /^Partial Credit Factor/) {
     $result.='<th>'.      $result.='<th>'.
                 &mt('Score Part: [_1][_2](weight = [_3])',   &mt('Score Part: [_1][_2](weight = [_3])',
                     $display_part,'<br />',$weight{$partid}).'</th>'."\n";      $display_part,'<br />',$weight{$partid}).'</th>'."\n";
     next;      next;
           
  } else {   } else {
Line 4210  sub viewgrades { Line 5235  sub viewgrades {
  return $a cmp $b;   return $a cmp $b;
      } (keys(%$fullname))) {       } (keys(%$fullname))) {
  $result.=&viewstudentgrade($symb,$env{'request.course.id'},   $result.=&viewstudentgrade($symb,$env{'request.course.id'},
    $_,$$fullname{$_},\@parts,\%weight,\$ctr,\%last_resets);     $_,$$fullname{$_},\@parts,\%weight,\$ctr,\%last_resets,$is_tool);
     }      }
     $result.=&Apache::loncommon::end_data_table();      $result.=&Apache::loncommon::end_data_table();
     $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";      $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";
Line 4227  sub viewgrades { Line 5252  sub viewgrades {
                                    $stu_status);                                     $stu_status);
                 } elsif (grep(/^none$/,@groups)) {                  } elsif (grep(/^none$/,@groups)) {
                     $result .= &mt('There are no students with no group assigned and with enrollment status [_1] to modify or grade.',                      $result .= &mt('There are no students with no group assigned and with enrollment status [_1] to modify or grade.',
                                    $stu_status);                                     $stu_status); 
                 } else {                  } else {
                     $result .= &mt('There are no students in group(s) [_1] with enrollment status [_2] to modify or grade.',                      $result .= &mt('There are no students in group(s) [_1] with enrollment status [_2] to modify or grade.',
                                    $group_display,$stu_status);                                     $group_display,$stu_status);
Line 4280  sub viewgrades { Line 5305  sub viewgrades {
                 }                  }
             } else {              } else {
                 if (grep(/^all$/,@groups)) {                  if (grep(/^all$/,@groups)) {
                     $result .= &mt('There are no students in section(s) [_1] with enrollment status [_2] and submission status "[_3]" to modify or grade.',              $result .= &mt('There are no students in section(s) [_1] with enrollment status [_2] and submission status "[_3]" to modify or grade.',
                                    $section_display,$stu_status,$submission_status);                             $section_display,$stu_status,$submission_status);
                 } elsif (grep(/^none$/,@groups)) {                  } elsif (grep(/^none$/,@groups)) {
                     $result .= &mt('There are no students in section(s) [_1] and no group with enrollment status [_2] and submission status "[_3]" to modify or grade.',                      $result .= &mt('There are no students in section(s) [_1] and no group with enrollment status [_2] and submission status "[_3]" to modify or grade.',
                                    $section_display,$stu_status,$submission_status);                                     $section_display,$stu_status,$submission_status);
Line 4290  sub viewgrades { Line 5315  sub viewgrades {
                                    $section_display,$group_display,$stu_status,$submission_status);                                     $section_display,$group_display,$stu_status,$submission_status);
                 }                  }
             }              }
  }          }
  $result .= '</span><br />';   $result .= '</span><br />';
     }      }
     return $result;      return $result;
 }  }
   
 #--- call by previous routine to display each student who satisfies submission filter.  #--- call by previous routine to display each student who satisfies submission filter. 
 sub viewstudentgrade {  sub viewstudentgrade {
     my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_;      my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets,$is_tool) = @_;
     my ($uname,$udom) = split(/:/,$student);      my ($uname,$udom) = split(/:/,$student);
     my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);      my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
     my $submitonly = $env{'form.submitonly'};      my $submitonly = $env{'form.submitonly'};
Line 4356  sub viewstudentgrade { Line 5381  sub viewstudentgrade {
         my ($aggtries,$totaltries);          my ($aggtries,$totaltries);
         unless (exists($aggregates{$part})) {          unless (exists($aggregates{$part})) {
     $totaltries = $record{'resource.'.$part.'.tries'};      $totaltries = $record{'resource.'.$part.'.tries'};
   
     $aggtries = $totaltries;      $aggtries = $totaltries;
             if ($$last_resets{$part}) {                if ($$last_resets{$part}) {  
                 $aggtries = &get_num_tries(\%record,$$last_resets{$part},                  $aggtries = &get_num_tries(\%record,$$last_resets{$part},
Line 4405  sub viewstudentgrade { Line 5429  sub viewstudentgrade {
 #    record does not get update if unchanged  #    record does not get update if unchanged
 sub editgrades {  sub editgrades {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
       my $toolsymb;
       if ($symb =~ /ext\.tool$/) {
           $toolsymb = $symb;
       }
   
     my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));      my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
     my $title='<h2>'.&mt('Current Grade Status').'</h2>';      my $title='<h2>'.&mt('Current Grade Status').'</h2>';
Line 4424  sub editgrades { Line 5452  sub editgrades {
     );      );
     my ($classlist,undef,$fullname) = &getclasslist($env{'form.section'},'0');      my ($classlist,undef,$fullname) = &getclasslist($env{'form.section'},'0');
   
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
       my %needpb = &passbacks_for_symb($cdom,$cnum,$symb);
   
     my (@partid);      my (@partid);
     my %weight = ();      my %weight = ();
     my %columns = ();      my %columns = ();
Line 4451  sub editgrades { Line 5483  sub editgrades {
     my ($part,$type) = &split_part_type($stores);      my ($part,$type) = &split_part_type($stores);
     if ($part !~ m/^\Q$partid\E/) { next;}      if ($part !~ m/^\Q$partid\E/) { next;}
     if ($type eq 'awarded' || $type eq 'solved') { next; }      if ($type eq 'awarded' || $type eq 'solved') { next; }
     my $display=&Apache::lonnet::metadata($url,$stores.'.display');      my $display=&Apache::lonnet::metadata($url,$stores.'.display',$toolsymb);
     $display =~ s/\[Part: \Q$part\E\]//;      $display =~ s/\[Part: \Q$part\E\]//;
             my $narrowtext = &mt('Tries');              my $narrowtext = &mt('Tries');
     $display =~ s/Number of Attempts/$narrowtext/;      $display =~ s/Number of Attempts/$narrowtext/;
Line 4474  sub editgrades { Line 5506  sub editgrades {
  &Apache::loncommon::end_data_table_header_row();   &Apache::loncommon::end_data_table_header_row();
     my @noupdate;      my @noupdate;
     my ($updateCtr,$noupdateCtr) = (1,1);      my ($updateCtr,$noupdateCtr) = (1,1);
       my ($got_types,%queueable,%pbsave,%skip_passback);
     for ($i=0; $i<$env{'form.total'}; $i++) {      for ($i=0; $i<$env{'form.total'}; $i++) {
  my $user = $env{'form.ctr'.$i};   my $user = $env{'form.ctr'.$i};
  my ($uname,$udom)=split(/:/,$user);   my ($uname,$udom)=split(/:/,$user);
  my %newrecord;   my %newrecord;
  my $updateflag = 0;   my $updateflag = 0;
         my $usec=$classlist->{"$uname:$udom"}[5];   my $usec=$classlist->{"$uname:$udom"}[5];
         my $canmodify = &canmodify($usec);   my $canmodify = &canmodify($usec);
         my $line = '<td'.($canmodify?'':' colspan="2"').'>'.   my $line = '<td'.($canmodify?'':' colspan="2"').'>'.
                    &nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';     &nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';
         if (!$canmodify) {   if (!$canmodify) {
             push(@noupdate,      push(@noupdate,
                  $line."<td colspan=\"$totcolspan\"><span class=\"LC_warning\">".   $line."<td colspan=\"$totcolspan\"><span class=\"LC_warning\">".
                  &mt('Not allowed to modify student')."</span></td>");   &mt('Not allowed to modify student')."</span></td>");
             next;      next;
         }   }
         my %aggregate = ();          my %aggregate = ();
         my $aggregateflag = 0;          my $aggregateflag = 0;
  $user=~s/:/_/; # colon doen't work in javascript for names   $user=~s/:/_/; # colon doen't work in javascript for names
           my (%weights,%awardeds,%excuseds);
  foreach (@partid) {   foreach (@partid) {
     my $old_aw    = $env{'form.GD_'.$user.'_'.$_.'_awarded_s'};      my $old_aw    = $env{'form.GD_'.$user.'_'.$_.'_awarded_s'};
     my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);      my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);
Line 4500  sub editgrades { Line 5534  sub editgrades {
     my $awarded   = $env{'form.GD_'.$user.'_'.$_.'_awarded'};      my $awarded   = $env{'form.GD_'.$user.'_'.$_.'_awarded'};
     my $pcr       = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1);      my $pcr       = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1);
     my $partial   = $awarded eq '' ? '' : $pcr;      my $partial   = $awarded eq '' ? '' : $pcr;
               $awardeds{$symb}{$_} = $partial;
     my $score;      my $score;
     if ($partial eq '') {      if ($partial eq '') {
  $score = $scoreptr{$env{'form.GD_'.$user.'_'.$_.'_solved_s'}};   $score = $scoreptr{$env{'form.GD_'.$user.'_'.$_.'_solved_s'}};
Line 4540  sub editgrades { Line 5575  sub editgrades {
   
   
     my $partid=$_;      my $partid=$_;
               if ($score eq 'excused') {
                   $excuseds{$symb}{$partid} = 1;
               } else {
                   $excuseds{$symb}{$partid} = '';
               }
     foreach my $stores (@parts) {      foreach my $stores (@parts) {
  my ($part,$type) = &split_part_type($stores);   my ($part,$type) = &split_part_type($stores);
  if ($part !~ m/^\Q$partid\E/) { next;}   if ($part !~ m/^\Q$partid\E/) { next;}
Line 4557  sub editgrades { Line 5597  sub editgrades {
  }   }
  $line.="\n";   $line.="\n";
   
  my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};  
  my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};  
   
  if ($updateflag) {   if ($updateflag) {
     $count++;      $count++;
     &Apache::lonnet::cstore(\%newrecord,$symb,$env{'request.course.id'},      &Apache::lonnet::cstore(\%newrecord,$symb,$env{'request.course.id'},
Line 4573  sub editgrades { Line 5610  sub editgrades {
      $udom,$uname);       $udom,$uname);
  my $all_graded = 1;   my $all_graded = 1;
  my $none_graded = 1;   my $none_graded = 1;
                   unless ($got_types) {
                       my $error;
                       my ($plist,$handgrd,$resptype) = &response_type($symb,\$error);
                       unless ($error) {
                           foreach my $part (@parts) {
                               if (ref($resptype->{$part}) eq 'HASH') {
                                   foreach my $id (keys(%{$resptype->{$part}})) {
                                       if (($resptype->{$part}->{$id} eq 'essay') ||
                                           (lc($handgrd->{$part.'_'.$id}) eq 'yes')) {
                                           $queueable{$part} = 1;
                                           last;
                                       }
                                   }
                               }
                           }
                       }
                       $got_types = 1;
                   }
  foreach my $part (@parts) {   foreach my $part (@parts) {
     if ( $record{'resource.'.$part.'.awarded'} eq '' ) {                      if ($queueable{$part}) {
  $all_graded = 0;          if ( $record{'resource.'.$part.'.awarded'} eq '' ) {
     } else {      $all_graded = 0;
  $none_graded = 0;          } else {
       $none_graded = 0;
           }
     }      }
  }                  }
   
  if ($all_graded || $none_graded) {   if ($all_graded || $none_graded) {
     &Apache::bridgetask::remove_from_queue('gradingqueue',      &Apache::bridgetask::remove_from_queue('gradingqueue',
    $symb,$cdom,$cnum,     $symb,$cdom,$cnum,
Line 4592  sub editgrades { Line 5648  sub editgrades {
  '<td align="right">&nbsp;'.$updateCtr.'&nbsp;</td>'.$line.   '<td align="right">&nbsp;'.$updateCtr.'&nbsp;</td>'.$line.
  &Apache::loncommon::end_data_table_row();   &Apache::loncommon::end_data_table_row();
     $updateCtr++;      $updateCtr++;
               if (keys(%needpb)) {
                   $weights{$symb} = \%weight;
                   &process_passbacks('editgrades',[$symb],$cdom,$cnum,$udom,$uname,\%weights,
                                      \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave);
               }
  } else {   } else {
     push(@noupdate,      push(@noupdate,
  '<td align="right">&nbsp;'.$noupdateCtr.'&nbsp;</td>'.$line);   '<td align="right">&nbsp;'.$noupdateCtr.'&nbsp;</td>'.$line);
Line 4644  sub split_part_type { Line 5705  sub split_part_type {
 #  #
 #--- Javascript to handle csv upload  #--- Javascript to handle csv upload
 sub csvupload_javascript_reverse_associate {  sub csvupload_javascript_reverse_associate {
     my $error1=&mt('You need to specify the username or the student/employee ID');      my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
     my $error2=&mt('You need to specify at least one grading field');      my $error2=&mt('You need to specify at least one grading field');
   &js_escape(\$error1);    &js_escape(\$error1);
   &js_escape(\$error2);    &js_escape(\$error2);
Line 4653  sub csvupload_javascript_reverse_associa Line 5714  sub csvupload_javascript_reverse_associa
     var foundsomething=0;      var foundsomething=0;
     var founduname=0;      var founduname=0;
     var foundID=0;      var foundID=0;
       var foundclicker=0;
     for (i=0;i<=vf.nfields.value;i++) {      for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');        tw=eval('vf.f'+i+'.selectedIndex');
       if (i==0 && tw!=0) { foundID=1; }        if (i==0 && tw!=0) { foundID=1; }
       if (i==1 && tw!=0) { founduname=1; }        if (i==1 && tw!=0) { founduname=1; }
       if (i!=0 && i!=1 && i!=2 && tw!=0) { foundsomething=1; }        if (i==2 && tw!=0) { foundclicker=1; }
         if (i!=0 && i!=1 && i!=2 && i!=3 && tw!=0) { foundsomething=1; }
     }      }
     if (founduname==0 && foundID==0) {      if (founduname==0 && foundID==0 && foundclicker==0) {
  alert('$error1');   alert('$error1');
  return;   return;
     }      }
Line 4686  ENDPICK Line 5749  ENDPICK
 }  }
   
 sub csvupload_javascript_forward_associate {  sub csvupload_javascript_forward_associate {
     my $error1=&mt('You need to specify the username or the student/employee ID');      my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
     my $error2=&mt('You need to specify at least one grading field');      my $error2=&mt('You need to specify at least one grading field');
   &js_escape(\$error1);    &js_escape(\$error1);
   &js_escape(\$error2);    &js_escape(\$error2);
Line 4695  sub csvupload_javascript_forward_associa Line 5758  sub csvupload_javascript_forward_associa
     var foundsomething=0;      var foundsomething=0;
     var founduname=0;      var founduname=0;
     var foundID=0;      var foundID=0;
       var foundclicker=0;
     for (i=0;i<=vf.nfields.value;i++) {      for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');        tw=eval('vf.f'+i+'.selectedIndex');
       if (tw==1) { foundID=1; }        if (tw==1) { foundID=1; }
       if (tw==2) { founduname=1; }        if (tw==2) { founduname=1; }
       if (tw>3) { foundsomething=1; }        if (tw==3) { foundclicker=1; }
         if (tw>4) { foundsomething=1; }
     }      }
     if (founduname==0 && foundID==0) {      if (founduname==0 && foundID==0 && Æ’oundclicker==0) {
  alert('$error1');   alert('$error1');
  return;   return;
     }      }
Line 4733  sub csvuploadmap_header { Line 5798  sub csvuploadmap_header {
  $javascript=&csvupload_javascript_forward_associate();   $javascript=&csvupload_javascript_forward_associate();
     }      }
   
     my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');  
     my $ignore=&mt('Ignore First Line');  
     $symb = &Apache::lonenc::check_encrypt($symb);      $symb = &Apache::lonenc::check_encrypt($symb);
     $request->print('<form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">'.      $request->print('<form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">'.
                     &mt('Total number of records found in file: [_1]',$distotal).'<hr />'.                      &mt('Total number of records found in file: [_1]',$distotal).'<hr />'.
Line 4743  sub csvuploadmap_header { Line 5806  sub csvuploadmap_header {
     $request->print(<<ENDPICK);      $request->print(<<ENDPICK);
 <br />  <br />
 <input type="button" value="$reverse" onclick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />  <input type="button" value="$reverse" onclick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
 <label><input type="checkbox" name="noFirstLine" $checked />$ignore</label>  
 <input type="hidden" name="associate"  value="" />  <input type="hidden" name="associate"  value="" />
 <input type="hidden" name="phase"      value="three" />  <input type="hidden" name="phase"      value="three" />
 <input type="hidden" name="datatoken"  value="$datatoken" />  <input type="hidden" name="datatoken"  value="$datatoken" />
Line 4762  ENDPICK Line 5824  ENDPICK
   
 sub csvupload_fields {  sub csvupload_fields {
     my ($symb,$errorref) = @_;      my ($symb,$errorref) = @_;
       my $toolsymb;
       if ($symb =~ /ext\.tool$/) {
           $toolsymb = $symb;
       }
     my (@parts) = &getpartlist($symb,$errorref);      my (@parts) = &getpartlist($symb,$errorref);
     if (ref($errorref)) {      if (ref($errorref)) {
         if ($$errorref) {          if ($$errorref) {
Line 4771  sub csvupload_fields { Line 5837  sub csvupload_fields {
   
     my @fields=(['ID','Student/Employee ID'],      my @fields=(['ID','Student/Employee ID'],
  ['username','Student Username'],   ['username','Student Username'],
    ['clicker','Clicker ID'],
  ['domain','Student Domain']);   ['domain','Student Domain']);
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     foreach my $part (sort(@parts)) {      foreach my $part (sort(@parts)) {
  my @datum;   my @datum;
  my $display=&Apache::lonnet::metadata($url,$part.'.display');   my $display=&Apache::lonnet::metadata($url,$part.'.display',$toolsymb);
  my $name=$part;   my $name=$part;
  if  (!$display) { $display = $name; }   if (!$display) { $display = $name; }
  @datum=($name,$display);   @datum=($name,$display);
  if ($name=~/^stores_(.*)_awarded/) {   if ($name=~/^stores_(.*)_awarded/) {
     push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);      push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);
Line 4833  sub upcsvScores_form { Line 5900  sub upcsvScores_form {
 <input type="hidden" name="command" value="csvuploadmap" />  <input type="hidden" name="command" value="csvuploadmap" />
 $upfile_select  $upfile_select
 <br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />  <br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />
 <label><input type="checkbox" name="noFirstLine" />$ignore</label>  
 </form>  </form>
 ENDUPFORM  ENDUPFORM
     $result.=&Apache::loncommon::help_open_topic("Course_Convert_To_CSV",      $result.=&Apache::loncommon::help_open_topic("Course_Convert_To_CSV",
                            &mt("How do I create a CSV file from a spreadsheet")).                             &mt("How do I create a CSV file from a spreadsheet")).
             '</td>'.               '</td>'.
             &Apache::loncommon::end_data_table_row().              &Apache::loncommon::end_data_table_row().
             &Apache::loncommon::end_data_table();              &Apache::loncommon::end_data_table();
     return $result;      return $result;
Line 4853  sub csvuploadmap { Line 5919  sub csvuploadmap {
     if (!$env{'form.datatoken'}) {      if (!$env{'form.datatoken'}) {
  $datatoken=&Apache::loncommon::upfile_store($request);   $datatoken=&Apache::loncommon::upfile_store($request);
     } else {      } else {
         $datatoken=&Apache::loncommon::valid_datatoken($env{'form.datatoken'});   $datatoken=&Apache::loncommon::valid_datatoken($env{'form.datatoken'});
         if ($datatoken ne '') {           if ($datatoken ne '') {
     &Apache::loncommon::load_tmp_file($request,$datatoken);      &Apache::loncommon::load_tmp_file($request,$datatoken);
         }          }
     }      }
     my @records=&Apache::loncommon::upfile_record_sep();      my @records=&Apache::loncommon::upfile_record_sep();
     if ($env{'form.noFirstLine'}) { shift(@records); }  
     &csvuploadmap_header($request,$symb,$datatoken,$#records+1);      &csvuploadmap_header($request,$symb,$datatoken,$#records+1);
     my ($i,$keyfields);      my ($i,$keyfields);
     if (@records) {      if (@records) {
Line 4896  sub csvuploadmap { Line 5961  sub csvuploadmap {
 sub csvuploadoptions {  sub csvuploadoptions {
     my ($request,$symb)= @_;      my ($request,$symb)= @_;
     my $overwrite=&mt('Overwrite any existing score');      my $overwrite=&mt('Overwrite any existing score');
     my $checked=(($env{'form.noFirstLine'})?'1':'0');  
     my $ignore=&mt('Ignore First Line');  
     $request->print(<<ENDPICK);      $request->print(<<ENDPICK);
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 <input type="hidden" name="command"    value="csvuploadassign" />  <input type="hidden" name="command"    value="csvuploadassign" />
Line 4911  ENDPICK Line 5974  ENDPICK
     my %fields=&get_fields();      my %fields=&get_fields();
     if (!defined($fields{'domain'})) {      if (!defined($fields{'domain'})) {
  my $domform = &Apache::loncommon::select_dom_form($env{'request.role.domain'},'default_domain');   my $domform = &Apache::loncommon::select_dom_form($env{'request.role.domain'},'default_domain');
         $request->print("\n<p>".&mt('Users are in domain: [_1]',$domform)."</p>\n");   $request->print("\n<p>".&mt('Users are in domain: [_1]',$domform)."</p>\n");
     }      }
     foreach my $key (sort(keys(%env))) {      foreach my $key (sort(keys(%env))) {
  if ($key !~ /^form\.(.*)$/) { next; }   if ($key !~ /^form\.(.*)$/) { next; }
Line 4949  sub csvuploadassign { Line 6012  sub csvuploadassign {
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $error_msg = '';      my $error_msg = '';
     my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'});      my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'});
     if ($datatoken ne '') {      if ($datatoken ne '') { 
         &Apache::loncommon::load_tmp_file($request,$datatoken);          &Apache::loncommon::load_tmp_file($request,$datatoken);
     }      }
     my @gradedata = &Apache::loncommon::upfile_record_sep();      my @gradedata = &Apache::loncommon::upfile_record_sep();
     if ($env{'form.noFirstLine'}) { shift(@gradedata); }  
     my %fields=&get_fields();      my %fields=&get_fields();
     my $courseid=$env{'request.course.id'};      my $courseid=$env{'request.course.id'};
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
     my ($classlist) = &getclasslist('all',0);      my ($classlist) = &getclasslist('all',0);
     my @notallowed;      my @notallowed;
     my @skipped;      my @skipped;
     my @warnings;      my @warnings;
     my $countdone=0;      my $countdone=0;
       my @parts;
       my %needpb = &passbacks_for_symb($cdom,$cnum,$symb);
       my $passback;
       if (keys(%needpb)) {
           $passback = 1;
           my $navmap = Apache::lonnavmaps::navmap->new();
           if (ref($navmap)) {
               my $res = $navmap->getBySymb($symb);
               if (ref($res)) {
                   my $partlist = $res->parts();
                   if (ref($partlist) eq 'ARRAY') {
                       @parts = sort(@{$partlist});
                   }
               }
           } else {
               return &navmap_errormsg();
           }
       }
       my (%skip_passback,%pbsave,%weights,%awardeds,%excuseds);
   
     foreach my $grade (@gradedata) {      foreach my $grade (@gradedata) {
  my %entries=&Apache::loncommon::record_sep($grade);   my %entries=&Apache::loncommon::record_sep($grade);
  my $domain;   my $domain;
Line 4975  sub csvuploadassign { Line 6059  sub csvuploadassign {
  if (!$username) {   if (!$username) {
     my $id=$entries{$fields{'ID'}};      my $id=$entries{$fields{'ID'}};
     $id=~s/\s//g;      $id=~s/\s//g;
     my %ids=&Apache::lonnet::idget($domain,$id);              if ($id ne '') {
     $username=$ids{$id};          my %ids=&Apache::lonnet::idget($domain,[$id]);
           $username=$ids{$id};
               } else {
                   if ($entries{$fields{'clicker'}}) {
                       my $clicker = $entries{$fields{'clicker'}};
                       $clicker=~s/\s//g;
                       if ($clicker ne '') {
                           my %clickers = &Apache::lonnet::idget($domain,[$clicker],'clickers');
                           if ($clickers{$clicker} ne '') {  
                               my $match = 0;
                               my @inclass;
                               foreach my $poss (split(/,/,$clickers{$clicker})) {
                                   if (exists($$classlist{"$poss:$domain"})) {
                                       $username = $poss;
                                       push(@inclass,$poss);
                                       $match ++;
                                       
                                   }
                               }
                               if ($match > 1) {
                                   undef($username); 
                                   $request->print('<p class="LC_warning">'.
                                                   &mt('Score not saved for clicker: [_1] (matched multiple usernames: [_2])',
                                                   $clicker,join(', ',@inclass)).'</p>');
                               }
                           }
                       }
                   }
               }
  }   }
  if (!exists($$classlist{"$username:$domain"})) {   if (!exists($$classlist{"$username:$domain"})) {
     my $id=$entries{$fields{'ID'}};      my $id=$entries{$fields{'ID'}};
     $id=~s/\s//g;      $id=~s/\s//g;
     if ($id) {              my $clicker = $entries{$fields{'clicker'}};
               $clicker=~s/\s//g;
               if ($clicker) {
                   push(@skipped,"$clicker:$domain");
       } elsif ($id) {
  push(@skipped,"$id:$domain");   push(@skipped,"$id:$domain");
     } else {      } else {
  push(@skipped,"$username:$domain");   push(@skipped,"$username:$domain");
Line 5003  sub csvuploadassign { Line 6119  sub csvuploadassign {
  my $part=$1;   my $part=$1;
  my $wgt =&Apache::lonnet::EXT('resource.'.$part.'.weight',   my $wgt =&Apache::lonnet::EXT('resource.'.$part.'.weight',
       $symb,$domain,$username);        $symb,$domain,$username);
                   $weights{$symb}{$part} = $wgt;
                 if ($wgt) {                  if ($wgt) {
                     $entries{$fields{$dest}}=~s/\s//g;                      $entries{$fields{$dest}}=~s/\s//g;
                     my $pcr=$entries{$fields{$dest}} / $wgt;                      my $pcr=$entries{$fields{$dest}} / $wgt;
                       if ($passback) {
                           $awardeds{$symb}{$part} = $pcr;
                           $excuseds{$symb}{$part} = '';
                       }
                     my $award=($pcr == 0) ? 'incorrect_by_override'                      my $award=($pcr == 0) ? 'incorrect_by_override'
                                           : 'correct_by_override';                                            : 'correct_by_override';
                     if ($pcr>1) {                      if ($pcr>1) {
                         push(@warnings,&mt("[_1]: point value larger than weight","$username:$domain"));                         push(@warnings,&mt("[_1]: point value larger than weight","$username:$domain"));
                     }                      }
                     $grades{"resource.$part.awarded"}=$pcr;                      $grades{"resource.$part.awarded"}=$pcr;
                     $grades{"resource.$part.solved"}=$award;                      $grades{"resource.$part.solved"}=$award;
Line 5025  sub csvuploadassign { Line 6146  sub csvuploadassign {
  if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }   if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }
  if ($dest=~/stores_(.*)_solved/)  { if ($points{$1}) {next;} }   if ($dest=~/stores_(.*)_solved/)  { if ($points{$1}) {next;} }
  my $store_key=$dest;   my $store_key=$dest;
                   if ($passback) {
                       if ($store_key=~/stores_(.*)_(awarded|solved)/) {
                           my ($part,$key) = ($1,$2);
                           unless ((ref($weights{$symb}) eq 'HASH') && (exists($weights{$symb}{$part}))) {
                               $weights{$symb}{$part} = &Apache::lonnet::EXT('resource.'.$part.'.weight',
                                                                             $symb,$domain,$username);
                           }
                           if ($key eq 'awarded') {
                               $awardeds{$symb}{$part} = $entries{$fields{$dest}};
                           } elsif ($key eq 'solved') {
                               if ($entries{$fields{$dest}} =~ /^excused/) {
                                   $excuseds{$symb}{$part} = 1;
                               }
                           }
                       }
                   }
  $store_key=~s/^stores/resource/;   $store_key=~s/^stores/resource/;
  $store_key=~s/_/\./g;   $store_key=~s/_/\./g;
  $grades{$store_key}=$entries{$fields{$dest}};   $grades{$store_key}=$entries{$fields{$dest}};
Line 5041  sub csvuploadassign { Line 6178  sub csvuploadassign {
 # Successfully stored  # Successfully stored
       $request->print('.');        $request->print('.');
 # Remove from grading queue  # Remove from grading queue
               &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,                &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,$cdom,$cnum,
                                              $env{'course.'.$env{'request.course.id'}.'.domain'},       $domain,$username);
                                              $env{'course.'.$env{'request.course.id'}.'.num'},                $countdone++;
                                              $domain,$username);                if ($passback) {
    } else {                    my @parts_in_upload;
                     if (ref($weights{$symb}) eq 'HASH') {
                         @parts_in_upload = sort(keys(%{$weights{$symb}}));
                     }
                     my @diffs = &Apache::loncommon::compare_arrays(\@parts_in_upload,\@parts);
                     if (@diffs > 0) {
                         my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$username);
                         foreach my $part (@parts) {
                             next if (grep(/^\Q$part\E$/,@parts_in_upload));
                             $weights{$symb}{$part} = &Apache::lonnet::EXT('resource.'.$part.'.weight',
                                                                           $symb,$domain,$username);
                             if ($record{"resource.$part.solved"} =~/^excused/) {
                                 $excuseds{$symb}{$part} = 1;
                             } else {
                                 $excuseds{$symb}{$part} = '';
                             }
                             $awardeds{$symb}{$part} = $record{"resource.$part.awarded"};
                         }
                     }
                     &process_passbacks('csvupload',[$symb],$cdom,$cnum,$domain,$username,\%weights,
                                        \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave);
                 }
              } else {
       $request->print("<p><span class=\"LC_error\">".        $request->print("<p><span class=\"LC_error\">".
                               &mt("Failed to save data for student [_1]. Message when trying to save was: [_2]",                                &mt("Failed to save data for student [_1]. Message when trying to save was: [_2]",
                                   "$username:$domain",$result)."</span></p>");                                    "$username:$domain",$result)."</span></p>");
    }     }
    $request->rflush();     $request->rflush();
    $countdone++;  
         }          }
     }      }
     $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt("Saved scores for [quant,_1,student]",$countdone),$countdone==0));      $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt("Saved scores for [quant,_1,student]",$countdone),$countdone==0));
Line 5120  LISTJAVASCRIPT Line 6278  LISTJAVASCRIPT
     # Collection of hidden fields      # Collection of hidden fields
     my $ctr=0;      my $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);          my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
  $result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";          $result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";
  $result.='<input type="hidden" name="title'.$ctr.'" value="'.$showtitle.'" />'."\n";          $result.='<input type="hidden" name="title'.$ctr.'" value="'.$showtitle.'" />'."\n";
  $ctr++;          $ctr++;
     }      }
     $result.='<input type="hidden" name="page" />'."\n".      $result.='<input type="hidden" name="page" />'."\n".
  '<input type="hidden" name="title" />'."\n";          '<input type="hidden" name="title" />'."\n";
   
     $result.=&build_section_inputs();      $result.=&build_section_inputs();
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));      my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
     $result.='<input type="hidden" name="Status"  value="'.$stu_status.'" />'."\n".      $result.='<input type="hidden" name="Status"  value="'.$stu_status.'" />'."\n".
         '<input type="hidden" name="command" value="displayPage" />'."\n".   '<input type="hidden" name="command" value="displayPage" />'."\n".
         '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";   '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
   
     # Show grading options      # Show grading options
     $result.=&Apache::lonhtmlcommon::start_pick_box();      $result.=&Apache::lonhtmlcommon::start_pick_box();
     my $select = '<select name="selectpage">'."\n";      my $select = '<select name="selectpage">'."\n";
     $ctr=0;      $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
         my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);   my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
         $select.='<option value="'.$ctr.'"'.   $select.='<option value="'.$ctr.'"'.
             ($$symbx{$_} =~ /$curpage$/ ? ' selected="selected"' : '').      ($$symbx{$_} =~ /$curpage$/ ? ' selected="selected"' : '').
             '>'.$showtitle.'</option>'."\n";      '>'.$showtitle.'</option>'."\n";
         $ctr++;   $ctr++;
     }      }
     $select.= '</select>';      $select.= '</select>';
   
Line 5169  LISTJAVASCRIPT Line 6327  LISTJAVASCRIPT
        .'<label><input type="radio" name="lastSub" value="all" /> '         .'<label><input type="radio" name="lastSub" value="all" /> '
            .&mt('all submissions with details').' </label>'             .&mt('all submissions with details').' </label>'
        .&Apache::lonhtmlcommon::row_closure();         .&Apache::lonhtmlcommon::row_closure();
       
     $result.=      $result.=
         &Apache::lonhtmlcommon::row_title(&mt('Use CODE'))          &Apache::lonhtmlcommon::row_title(&mt('Use CODE'))
        .'<input type="text" name="CODE" value="" />'         .'<input type="text" name="CODE" value="" />'
Line 5241  sub getSymbMap { Line 6399  sub getSymbMap {
     my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },      my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },
        1,0,1);         1,0,1);
     for my $sequence ($navmap->getById('0.0'), @sequences) {      for my $sequence ($navmap->getById('0.0'), @sequences) {
  if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {   if ($navmap->hasResource($sequence, sub { shift->is_gradable(); }, 0) ) {
     my $title = $minder.'.'.      my $title = $minder.'.'.
  &HTML::Entities::encode($sequence->compTitle(),'"\'&');   &HTML::Entities::encode($sequence->compTitle(),'"\'&');
     push(@titles, $title); # minder in case two titles are identical      push(@titles, $title); # minder in case two titles are identical
Line 5272  sub displayPage { Line 6430  sub displayPage {
     &Apache::lonnet::clear_EXT_cache_status();      &Apache::lonnet::clear_EXT_cache_status();
   
     if (!&canview($usec)) {      if (!&canview($usec)) {
  $request->print(          $request->print(
             '<span class="LC_warning">'.              '<span class="LC_warning">'.
             &mt('Unable to view requested student. ([_1])',              &mt('Unable to view requested student. ([_1])',
                 $env{'form.student'}).                      $env{'form.student'}).
             '</span>');              '</span>');
         return;          return;
     }      }
Line 5338  sub displayPage { Line 6496  sub displayPage {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }          if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }          if($curRes == $iterator->END_MAP) { $depth--; }
   
         if (ref($curRes) && $curRes->is_problem()) {          if (ref($curRes) && $curRes->is_gradable()) {
     my $parts = $curRes->parts();      my $parts = $curRes->parts();
             my $title = $curRes->compTitle();              my $title = $curRes->compTitle();
     my $symbx = $curRes->symb();      my $symbx = $curRes->symb();
               my $is_tool = ($symbx =~ /ext\.tool$/);
     $studentTable.=      $studentTable.=
  &Apache::loncommon::start_data_table_row().   &Apache::loncommon::start_data_table_row().
  '<td align="center" valign="top" >'.$prob.   '<td align="center" valign="top" >'.$prob.
Line 5352  sub displayPage { Line 6511  sub displayPage {
  '</td>';   '</td>';
     $studentTable.='<td valign="top">';      $studentTable.='<td valign="top">';
     my %form = ('CODE' => $env{'form.CODE'},);      my %form = ('CODE' => $env{'form.CODE'},);
     if ($env{'form.vProb'} eq 'yes' ) {              if ($is_tool) {
  $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,                  $studentTable.='&nbsp;<b>'.$title.'</b><br />';
      undef,'both',\%form);              } else {
     } else {          if ($env{'form.vProb'} eq 'yes' ) {
  my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);      $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
  $companswer =~ s|<form(.*?)>||g;           undef,'both',\%form);
  $companswer =~ s|</form>||g;          } else {
 # while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>      my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);
 #    $companswer =~ s/$1/ /ms;      $companswer =~ s|<form(.*?)>||g;
 #    $request->print('match='.$1."<br />\n");      $companswer =~ s|</form>||g;
 # }  #    while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
 # $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;  #        $companswer =~ s/$1/ /ms;
  $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>'.&mt('Correct answer').':</b><br />'.$companswer;  #        $request->print('match='.$1."<br />\n");
   #    }
   #    $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
       $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>'.&mt('Correct answer').':</b><br />'.$companswer;
    }
     }      }
   
     my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);      my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
   
     if ($env{'form.lastSub'} eq 'datesub') {      if ($env{'form.lastSub'} eq 'datesub') {
  if ($record{'version'} eq '') {   if ($record{'version'} eq '') {
     $studentTable.='<br />&nbsp;<span class="LC_warning">'.&mt('No recorded submission for this problem.').'</span><br />';                      my $msg = &mt('No recorded submission for this problem.');
                       if ($is_tool) {
                           $msg = &mt('No recorded transactions for this external tool');
                       }
       $studentTable.='<br />&nbsp;<span class="LC_warning">'.$msg.'</span><br />';
  } else {   } else {
     my %responseType = ();      my %responseType = ();
     foreach my $partid (@{$parts}) {      foreach my $partid (@{$parts}) {
Line 5384  sub displayPage { Line 6551  sub displayPage {
  $responseType{$partid} = \%responseIds;   $responseType{$partid} = \%responseIds;
     }      }
     $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);      $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);
   
  }   }
     } elsif ($env{'form.lastSub'} eq 'all') {      } elsif ($env{'form.lastSub'} eq 'all') {
  my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');   my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
                 my $identifier = (&canmodify($usec)? $prob : '');                  my $identifier = (&canmodify($usec)? $prob : ''); 
  $studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,   $studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,
  $env{'request.course.id'},   $env{'request.course.id'},
  '','.submission',undef,   '','.submission',undef,
Line 5429  sub displaySubByDates { Line 6595  sub displaySubByDates {
     my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;      my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;
     my $isCODE=0;      my $isCODE=0;
     my $isTask = ($symb =~/\.task$/);      my $isTask = ($symb =~/\.task$/);
       my $is_tool = ($symb =~/\.tool$/);
     if (exists($record->{'resource.CODE'})) { $isCODE=1; }      if (exists($record->{'resource.CODE'})) { $isCODE=1; }
     my $studentTable=&Apache::loncommon::start_data_table().      my $studentTable=&Apache::loncommon::start_data_table().
  &Apache::loncommon::start_data_table_header_row().   &Apache::loncommon::start_data_table_header_row().
  '<th>'.&mt('Date/Time').'</th>'.   '<th>'.&mt('Date/Time').'</th>'.
  ($isCODE?'<th>'.&mt('CODE').'</th>':'').   ($isCODE?'<th>'.&mt('CODE').'</th>':'').
         ($isTask?'<th>'.&mt('Version').'</th>':'').          ($isTask?'<th>'.&mt('Version').'</th>':'').
  '<th>'.&mt('Submission').'</th>'.   '<th>'.($is_tool?&mt('Grade'):&mt('Submission')).'</th>'.
  '<th>'.&mt('Status').'</th>'.   '<th>'.&mt('Status').'</th>'.
  &Apache::loncommon::end_data_table_header_row();   &Apache::loncommon::end_data_table_header_row();
     my ($version);      my ($version);
Line 5443  sub displaySubByDates { Line 6610  sub displaySubByDates {
     my %orders;      my %orders;
     $mark{'correct_by_student'} = $checkIcon;      $mark{'correct_by_student'} = $checkIcon;
     if (!exists($$record{'1:timestamp'})) {      if (!exists($$record{'1:timestamp'})) {
  return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';          if ($is_tool) {
               return '<br />&nbsp;<span class="LC_warning">'.&mt('No grade passed back.').'</span><br />';
           } else {
               return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';
           }
     }      }
   
     my $interaction;      my $interaction;
Line 5476  sub displaySubByDates { Line 6647  sub displaySubByDates {
             if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {              if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {
                 $hidden = 1;                  $hidden = 1;
             }              }
     my @matchKey;              my @matchKey;
             if ($isTask) {              if ($isTask) {
                 @matchKey = sort(grep(/^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys));                  @matchKey = sort(grep(/^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys));
               } elsif ($is_tool) {
                   @matchKey = sort(grep(/^resource\.\Q$partid\E\.awarded$/,@versionKeys));
             } else {              } else {
  @matchKey = sort(grep(/^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));                  @matchKey = sort(grep(/^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));
             }              }
 #    next if ($$record{"$version:resource.$partid.solved"} eq '');  #    next if ($$record{"$version:resource.$partid.solved"} eq '');
     my $display_part=&get_display_part($partid,$symb);      my $display_part=&get_display_part($partid,$symb);
     foreach my $matchKey (@matchKey) {      foreach my $matchKey (@matchKey) {
  if (exists($$record{$version.':'.$matchKey}) &&   if (exists($$record{$version.':'.$matchKey}) &&
     $$record{$version.':'.$matchKey} ne '') {      $$record{$version.':'.$matchKey} ne '') {
                                           if ($is_tool) {
     my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)                          $displaySub[0].=$$record{"$version:resource.$partid.awarded"};
                : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));  
                     $displaySub[0].='<span class="LC_nobreak">';  
                     $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'  
                                    .' <span class="LC_internal_info">'  
                                    .'('.&mt('Response ID: [_1]',$responseId).')'  
                                    .'</span>'  
                                    .' <b>';  
                     if ($hidden) {  
                         $displaySub[0].= &mt('Anonymous Survey').'</b>';  
                     } else {                      } else {
                         my ($trial,$rndseed,$newvariation);          my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
                         if ($type eq 'randomizetry') {                     : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
                             $trial = $$record{"$where.$partid.tries"};                          $displaySub[0].='<span class="LC_nobreak">';
                             $rndseed = $$record{"$where.$partid.rndseed"};                          $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'
                         }                                         .' <span class="LC_internal_info">'
         if ($$record{"$where.$partid.tries"} eq '') {                                         .'('.&mt('Response ID: [_1]',$responseId).')'
     $displaySub[0].=&mt('Trial not counted');                                         .'</span>'
         } else {                                         .' <b>';
     $displaySub[0].=&mt('Trial: [_1]',                          if ($hidden) {
     $$record{"$where.$partid.tries"});                              $displaySub[0].= &mt('Anonymous Survey').'</b>';
                             if (($rndseed ne '')  && ($lastrndseed{$partid} ne '')) {                          } else {
                                 if (($rndseed ne $lastrndseed{$partid}) &&                              my ($trial,$rndseed,$newvariation);
                                     (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) {                              if ($type eq 'randomizetry') {
                                     $newvariation = '&nbsp;('.&mt('New variation this try').')';                                  $trial = $$record{"$where.$partid.tries"};
                                 }                                  $rndseed = $$record{"$where.$partid.rndseed"};
                             }                              }
                             $lastrndseed{$partid} = $rndseed;              if ($$record{"$where.$partid.tries"} eq '') {
                             $lasttype{$partid} = $type;          $displaySub[0].=&mt('Trial not counted');
         }              } else {
         my $responseType=($isTask ? 'Task'          $displaySub[0].=&mt('Trial: [_1]',
           $$record{"$where.$partid.tries"});
                                   if (($rndseed ne '') && ($lastrndseed{$partid} ne '')) {
                                       if (($rndseed ne $lastrndseed{$partid}) &&
                                           (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) {
                                           $newvariation = '&nbsp;('.&mt('New variation this try').')';
                                       }
                                   }
                                   $lastrndseed{$partid} = $rndseed;
                                   $lasttype{$partid} = $type;
               }
               my $responseType=($isTask ? 'Task'
                                               : $responseType->{$partid}->{$responseId});                                                : $responseType->{$partid}->{$responseId});
         if (!exists($orders{$partid})) { $orders{$partid}={}; }              if (!exists($orders{$partid})) { $orders{$partid}={}; }
         if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {              if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {
     $orders{$partid}->{$responseId}=          $orders{$partid}->{$responseId}=
         &get_order($partid,$responseId,$symb,$uname,$udom,              &get_order($partid,$responseId,$symb,$uname,$udom,
                                            $no_increment,$type,$trial,$rndseed);                                                 $no_increment,$type,$trial,$rndseed);
         }              }
         $displaySub[0].='</b>'.$newvariation.'</span>'; # /nobreak              $displaySub[0].='</b>'.$newvariation.'</span>'; # /nobreak
         $displaySub[0].='&nbsp; '.              $displaySub[0].='&nbsp; '.
     &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).'<br />';          &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).'<br />';
                           }
                     }                      }
  }   }
     }      }
Line 5543  sub displaySubByDates { Line 6719  sub displaySubByDates {
     lc($$record{"$where.$partid.award"}).' '.      lc($$record{"$where.$partid.award"}).' '.
     $mark{$$record{"$where.$partid.solved"}}.      $mark{$$record{"$where.$partid.solved"}}.
     '<br />';      '<br />';
       } elsif (($is_tool) && (exists($$record{"$version:resource.$partid.solved"}))) {
    if ($$record{"$version:resource.$partid.solved"} =~ /^(in|)correct_by_passback$/) {
       $displaySub[1].=&mt('Grade passed back by external tool');
    }
     }      }
     if (exists $$record{"$where.$partid.regrader"}) {      if (exists $$record{"$where.$partid.regrader"}) {
  $displaySub[2].=$$record{"$where.$partid.regrader"}.   $displaySub[2].=$$record{"$where.$partid.regrader"};
     ' (<b>'.&mt('Part').':</b> '.$display_part.')';   unless ($is_tool) {
       $displaySub[2].=' (<b>'.&mt('Part').':</b> '.$display_part.')';
    }
     } elsif ($$record{"$version:resource.$partid.regrader"} =~ /\S/) {      } elsif ($$record{"$version:resource.$partid.regrader"} =~ /\S/) {
  $displaySub[2].=   $displaySub[2].=
     $$record{"$version:resource.$partid.regrader"}.      $$record{"$version:resource.$partid.regrader"};
     ' (<b>'.&mt('Part').':</b> '.$display_part.')';                  unless ($is_tool) {
       $displaySub[2].=' (<b>'.&mt('Part').':</b> '.$display_part.')';
                   }
     }      }
  }   }
  # needed because old essay regrader has not parts info   # needed because old essay regrader has not parts info
Line 5615  sub updateGradeByPage { Line 6799  sub updateGradeByPage {
     $iterator->next(); # skip the first BEGIN_MAP      $iterator->next(); # skip the first BEGIN_MAP
     my $curRes = $iterator->next(); # for "current resource"      my $curRes = $iterator->next(); # for "current resource"
     my ($depth,$question,$prob,$changeflag,$hideflag)= (1,1,1,0,0);      my ($depth,$question,$prob,$changeflag,$hideflag)= (1,1,1,0,0);
       my (@updates,%weights,%excuseds,%awardeds,@symbs_in_map);
     while ($depth > 0) {      while ($depth > 0) {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }          if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }          if($curRes == $iterator->END_MAP) { $depth--; }
Line 5623  sub updateGradeByPage { Line 6808  sub updateGradeByPage {
     my $parts = $curRes->parts();      my $parts = $curRes->parts();
             my $title = $curRes->compTitle();              my $title = $curRes->compTitle();
     my $symbx = $curRes->symb();      my $symbx = $curRes->symb();
               push(@symbs_in_map,$symbx);
     $studentTable.=      $studentTable.=
  &Apache::loncommon::start_data_table_row().   &Apache::loncommon::start_data_table_row().
  '<td align="center" valign="top" >'.$prob.   '<td align="center" valign="top" >'.$prob.
Line 5635  sub updateGradeByPage { Line 6821  sub updateGradeByPage {
     my @displayPts=();      my @displayPts=();
             my %aggregate = ();              my %aggregate = ();
             my $aggregateflag = 0;              my $aggregateflag = 0;
               my %queueable;
             if ($env{'form.HIDE'.$prob}) {              if ($env{'form.HIDE'.$prob}) {
                 my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);                  my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
                 my ($version,$parts) = split(/:/,$env{'form.HIDE'.$prob},2);                  my ($version,$parts) = split(/:/,$env{'form.HIDE'.$prob},2);
                 my $numchgs = &makehidden($version,$parts,\%record,$symbx,$udom,$uname,1);                  my $numchgs = &makehidden($version,$parts,\%record,$symbx,$udom,$uname,1);
                   if ($numchgs) {
                       push(@updates,$symbx);
                   }
                 $hideflag += $numchgs;                  $hideflag += $numchgs;
             }              }
     foreach my $partid (@{$parts}) {      foreach my $partid (@{$parts}) {
  my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid};   my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid};
  my $oldpts = $env{'form.oldpts'.$question.'_'.$partid};   my $oldpts = $env{'form.oldpts'.$question.'_'.$partid};
                   my @types = $curRes->responseType($partid);
                   if (grep(/^essay$/,@types)) {
                       $queueable{$partid} = 1;
                   } else {
                       my @ids = $curRes->responseIds($partid);
                       for (my $i=0; $i < scalar(@ids); $i++) {
                           my $hndgrd = &Apache::lonnet::EXT('resource.'.$partid.'_'.$ids[$i].
                                                             '.handgrade',$symb);
                           if (lc($hndgrd) eq 'yes') {
                               $queueable{$partid} = 1;
                               last;
                           }
                       }
                   }
  my $wgt = $env{'form.WGT'.$question.'_'.$partid} != 0 ?    my $wgt = $env{'form.WGT'.$question.'_'.$partid} != 0 ? 
     $env{'form.WGT'.$question.'_'.$partid} : 1;      $env{'form.WGT'.$question.'_'.$partid} : 1;
                   $weights{$symbx}{$partid} = $wgt;
                   $excuseds{$symbx}{$partid} = '';
  my $partial = $newpts/$wgt;   my $partial = $newpts/$wgt;
  my $score;   my $score;
  if ($partial > 0) {   if ($partial > 0) {
Line 5658  sub updateGradeByPage { Line 6863  sub updateGradeByPage {
  if ($dropMenu eq 'excused') {   if ($dropMenu eq 'excused') {
     $partial = '';      $partial = '';
     $score = 'excused';      $score = 'excused';
                       $excuseds{$symbx}{$partid} = 1;
  } elsif ($dropMenu eq 'reset status'   } elsif ($dropMenu eq 'reset status'
  && $env{'form.solved'.$question.'_'.$partid} ne '') { #update only if previous record exists   && $env{'form.solved'.$question.'_'.$partid} ne '') { #update only if previous record exists
     $newrecord{'resource.'.$partid.'.tries'} = 0;      $newrecord{'resource.'.$partid.'.tries'} = 0;
Line 5685  sub updateGradeByPage { Line 6891  sub updateGradeByPage {
      (($score eq 'excused') ? 'excused' : $newpts).       (($score eq 'excused') ? 'excused' : $newpts).
     '&nbsp;<br />';      '&nbsp;<br />';
  $question++;   $question++;
                   if (($newpts eq '') || ($partial eq '')) {
                       $awardeds{$symbx}{$partid} = 0;
                   } else {
                       $awardeds{$symbx}{$partid} = $partial;
                   }
  next if ($dropMenu eq 'reset status' || ($newpts eq $oldpts && $score ne 'excused'));   next if ($dropMenu eq 'reset status' || ($newpts eq $oldpts && $score ne 'excused'));
   
  $newrecord{'resource.'.$partid.'.awarded'}  = $partial if $partial ne '';   $newrecord{'resource.'.$partid.'.awarded'}  = $partial if $partial ne '';
Line 5710  sub updateGradeByPage { Line 6921  sub updateGradeByPage {
    $env{'request.course.id'},     $env{'request.course.id'},
    $udom,$uname);     $udom,$uname);
  &check_and_remove_from_queue($parts,\%record,undef,$symbx,   &check_and_remove_from_queue($parts,\%record,undef,$symbx,
      $cdom,$cnum,$udom,$uname);       $cdom,$cnum,$udom,$uname,\%queueable);
     }      }
           
             if ($aggregateflag) {              if ($aggregateflag) {
Line 5724  sub updateGradeByPage { Line 6935  sub updateGradeByPage {
  &Apache::loncommon::end_data_table_row();   &Apache::loncommon::end_data_table_row();
   
     $prob++;      $prob++;
               if ($changeflag) {
                   push(@updates,$symbx);
               }
  }   }
         $curRes = $iterator->next();          $curRes = $iterator->next();
     }      }
Line 5737  sub updateGradeByPage { Line 6951  sub updateGradeByPage {
                      $hideflag).'<br />');                       $hideflag).'<br />');
     $request->print($hidemsg.$grademsg.$studentTable);      $request->print($hidemsg.$grademsg.$studentTable);
   
       if (@updates) {
           my (@allsymbs,$mapsymb,@recurseup,%parentmapsymbs,%possmappb,%possrespb);
           @allsymbs = @updates;
           if (ref($map)) {
               $mapsymb = $map->symb();
               push(@allsymbs,$mapsymb);
               @recurseup = $navmap->recurseup_maps($map->src,1);
           }
           if (@recurseup) {
               push(@allsymbs,@recurseup);
               map { $parentmapsymbs{$_} = 1; } @recurseup;
           }
           my %passback = &Apache::lonnet::get('nohist_linkprot_passback',\@allsymbs,$cdom,$cnum);
           my (%uniqsymbs,$use_symbs_in_map);
           if (keys(%passback)) {
               foreach my $possible (keys(%passback)) {
                   if (ref($passback{$possible}) eq 'HASH') {
                       if ($possible eq $mapsymb) {
                           foreach my $launcher (keys(%{$passback{$possible}})) {
                               $possmappb{$launcher} = 1;
                           }
                           $use_symbs_in_map = 1;
                       } elsif (exists($parentmapsymbs{$possible})) {
                           foreach my $launcher (keys(%{$passback{$possible}})) {
                               my ($linkuri,$linkprotector,$scope) = split(/\0/,$launcher);
                               if ($scope eq 'rec') {
                                   $possmappb{$launcher} = 1;
                                   $use_symbs_in_map = 1;
                               }
                           }
                       } elsif (grep(/^\Q$possible$\E$/,@updates)) {
                           foreach my $launcher (keys(%{$passback{$possible}})) {
                               $possrespb{$launcher} = 1;
                           }
                           $uniqsymbs{$possible} = 1;
                       }
                   }
               }
           }
           if ($use_symbs_in_map) {
               map { $uniqsymbs{$_} = 1; } @symbs_in_map;
           }
           my @posslaunchers;
           if (keys(%possmappb)) {
               push(@posslaunchers,keys(%possmappb));
           }
           if (keys(%possrespb)) {
               push(@posslaunchers,keys(%possrespb));
           }
           if (@posslaunchers) {
               my (%pbsave,%skip_passback,%needpb);
               my %pbids = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',\@posslaunchers,$udom,$uname);
               foreach my $key (keys(%pbids)) {
                   if (ref($pbids{$key}) eq 'ARRAY') {
                       $needpb{$key} = 1;
                   }
               }
               my @symbs = keys(%uniqsymbs);
               &process_passbacks('updatebypage',\@symbs,$cdom,$cnum,$udom,$uname,\%weights,
                                  \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave,\%pbids);
               if (@Apache::grades::ltipassback) {
                   unless ($registered_cleanup) {
                       my $handlers = $request->get_handlers('PerlCleanupHandler');
                       $request->set_handlers('PerlCleanupHandler' =>
                                              [\&Apache::grades::make_passback,@{$handlers}]);
                       $registered_cleanup=1;
                   }
               }
           }
       }
     return '';      return '';
 }  }
   
   sub make_passback {
       if (@Apache::grades::ltipassback) {
           my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
           my $ip = &Apache::lonnet::get_host_ip($lonhost);
           foreach my $item (@Apache::grades::ltipassback) {
               &Apache::lonhomework::run_passback($item,$lonhost,$ip);
           }
           undef(@Apache::grades::ltipassback);
       }
   }
   
 #-------- end of section for handling grading by page/sequence ---------  #-------- end of section for handling grading by page/sequence ---------
 #  #
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
Line 6052  sub scantron_selectphase { Line 7347  sub scantron_selectphase {
   
     $ssi_error = 0;      $ssi_error = 0;
   
     if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||      if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'}) {
         &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {  
   
         # Chunk of form to prompt for a scantron file upload.   # Chunk of form to prompt for a scantron file upload.
   
         $r->print('          $r->print('
     <br />');      <br />');
         my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
         my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};      my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
         my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');      my $csec= $env{'request.course.sec'};
         &js_escape(\$alertmsg);      my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');
         my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($cdom);      &js_escape(\$alertmsg);
         $r->print(&Apache::lonhtmlcommon::scripttag('      my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($cdom);
       $r->print(&Apache::lonhtmlcommon::scripttag('
     function checkUpload(formname) {      function checkUpload(formname) {
         if (formname.upfile.value == "") {   if (formname.upfile.value == "") {
             alert("'.$alertmsg.'");      alert("'.$alertmsg.'");
             return false;      return false;
         }   }
         formname.submit();   formname.submit();
     }'."\n".$formatjs));      }'."\n".$formatjs));
         $r->print('      $r->print('
               <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">                <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
                 '.$default_form_data.'                  '.$default_form_data.'
                 <input name="courseid" type="hidden" value="'.$cnum.'" />                  <input name="courseid" type="hidden" value="'.$cnum.'" />
                   <input name="coursesec" type="hidden" value="'.$csec.'" />
                 <input name="domainid" type="hidden" value="'.$cdom.'" />                  <input name="domainid" type="hidden" value="'.$cdom.'" />
                 <input name="command" value="scantronupload_save" type="hidden" />                  <input name="command" value="scantronupload_save" type="hidden" />
               '.&Apache::loncommon::start_data_table('LC_scantron_action').'                '.&Apache::loncommon::start_data_table('LC_scantron_action').'
Line 6087  sub scantron_selectphase { Line 7383  sub scantron_selectphase {
               '.&Apache::loncommon::start_data_table_row().'                '.&Apache::loncommon::start_data_table_row().'
             <td>              <td>
                 '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'<br />'."\n");                  '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'<br />'."\n");
         if ($formatoptions) {      if ($formatoptions) {
             $r->print('</td>          $r->print('</td>
                  '.&Apache::loncommon::end_data_table_row().'                   '.&Apache::loncommon::end_data_table_row().'
                  '.&Apache::loncommon::start_data_table_row().'                   '.&Apache::loncommon::start_data_table_row().'
                  <td>'.$formattitle.('&nbsp;'x2).$formatoptions.'                   <td>'.$formattitle.('&nbsp;'x2).$formatoptions.'
Line 6096  sub scantron_selectphase { Line 7392  sub scantron_selectphase {
                  '.&Apache::loncommon::end_data_table_row().'                   '.&Apache::loncommon::end_data_table_row().'
                  '.&Apache::loncommon::start_data_table_row().'                   '.&Apache::loncommon::start_data_table_row().'
                  <td>'                   <td>'
             );          );
         } else {      } else {
             $r->print(' <br />');          $r->print(' <br />');
         }      }
         $r->print('<input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />      $r->print('<input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
               </td>                </td>
              '.&Apache::loncommon::end_data_table_row().'               '.&Apache::loncommon::end_data_table_row().'
              '.&Apache::loncommon::end_data_table().'               '.&Apache::loncommon::end_data_table().'
              </form>'               </form>'
         );      );
   
     }      }
   
Line 6222  sub scantron_selectphase { Line 7518  sub scantron_selectphase {
 =item username_to_idmap  =item username_to_idmap
   
     creates a hash keyed by student/employee ID with values of the corresponding      creates a hash keyed by student/employee ID with values of the corresponding
     student username:domain.      student username:domain. If a single ID occurs for more than one student,
       the status of the student is checked, and if Active, the value in the hash
       will be set to the Active student.
   
   Arguments:    Arguments:
   
Line 6422  sub digits_to_letters { Line 7720  sub digits_to_letters {
   
 =item scantron_parse_scanline  =item scantron_parse_scanline
   
   Decodes a scanline from the selected scantron file    Decodes a scanline from the selected bubblesheet file
   
  Arguments:   Arguments:
     line             - The text of the scantron file line to process      line             - The text of the bubblesheet file line to process
     whichline        - Line number      whichline        - Line number
     scantron_config  - Hash describing the format of the scantron lines.      scantron_config  - Hash describing the format of the bubblesheet lines.
     scan_data        - Hash of extra information about the scanline      scan_data        - Hash of extra information about the scanline
                        (see scantron_getfile for more information)                         (see scantron_getfile for more information)
     just_header      - True if should not process question answers but only      just_header      - True if should not process question answers but only
Line 6452  sub digits_to_letters { Line 7750  sub digits_to_letters {
     totalref         - Ref of scalar used to score total number of bubble      totalref         - Ref of scalar used to score total number of bubble
                        lines needed for responses in a scan line (used when                         lines needed for responses in a scan line (used when
                        randompick in use.                          randompick in use. 
       
  Returns:   Returns:
    Hash containing the result of parsing the scanline     Hash containing the result of parsing the scanline
   
Line 6545  sub scantron_parse_scanline { Line 7843  sub scantron_parse_scanline {
                                          $partids_by_symb,$orderedforcode,                                           $partids_by_symb,$orderedforcode,
                                          $respnumlookup,$startline);                                           $respnumlookup,$startline);
         if ($total) {          if ($total) {
             $lastpos = $total*$$scantron_config{'Qlength'};              $lastpos = $total*$$scantron_config{'Qlength'}; 
         }          }
         if (ref($totalref)) {          if (ref($totalref)) {
             $$totalref = $total;              $$totalref = $total;
Line 6559  sub scantron_parse_scanline { Line 7857  sub scantron_parse_scanline {
         if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {          if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {
             $answers_needed = $bubble_lines_per_response{$respnumlookup->{$questnum}};              $answers_needed = $bubble_lines_per_response{$respnumlookup->{$questnum}};
         } else {          } else {
             $answers_needed = $bubble_lines_per_response{$questnum};      $answers_needed = $bubble_lines_per_response{$questnum};
         }          }
         my $answer_length  = ($$scantron_config{'Qlength'} * $answers_needed)          my $answer_length  = ($$scantron_config{'Qlength'} * $answers_needed)
                              || 1;                               || 1;
Line 6619  sub scantron_parse_scanline { Line 7917  sub scantron_parse_scanline {
 }  }
   
 sub get_master_seq {  sub get_master_seq {
     my ($resources,$master_seq,$symb_to_resource) = @_;      my ($resources,$master_seq,$symb_to_resource,$need_symb_in_map,$symb_for_examcode) = @_;
     return unless ((ref($resources) eq 'ARRAY') && (ref($master_seq) eq 'ARRAY') &&      return unless ((ref($resources) eq 'ARRAY') && (ref($master_seq) eq 'ARRAY') && 
                    (ref($symb_to_resource) eq 'HASH'));                     (ref($symb_to_resource) eq 'HASH'));
       if ($need_symb_in_map) {
           return unless (ref($symb_for_examcode) eq 'HASH');
       }
     my $resource_error;      my $resource_error;
     foreach my $resource (@{$resources}) {      foreach my $resource (@{$resources}) {
         my $ressymb;          my $ressymb;
Line 6629  sub get_master_seq { Line 7930  sub get_master_seq {
             $ressymb = $resource->symb();              $ressymb = $resource->symb();
             push(@{$master_seq},$ressymb);              push(@{$master_seq},$ressymb);
             $symb_to_resource->{$ressymb} = $resource;              $symb_to_resource->{$ressymb} = $resource;
               if ($need_symb_in_map) {
                   unless ($resource->is_map()) {
                       my $map=(&Apache::lonnet::decode_symb($ressymb))[0];
                       unless (exists($symb_for_examcode->{$map})) {
                           $symb_for_examcode->{$map} = $ressymb;
                       }
                   }
               }
         } else {          } else {
             $resource_error = 1;              $resource_error = 1;
             last;              last;
Line 6698  sub scantron_validator_lettnum { Line 8007  sub scantron_validator_lettnum {
     my $occurrences = 0;      my $occurrences = 0;
     my $responsenum = $questnum-1;      my $responsenum = $questnum-1;
     if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {      if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {
        $responsenum = $respnumlookup->{$questnum-1}         $responsenum = $respnumlookup->{$questnum-1} 
     }      }
     if (($responsetype_per_response{$responsenum} eq 'essayresponse') ||      if (($responsetype_per_response{$responsenum} eq 'essayresponse') ||
         ($responsetype_per_response{$responsenum} eq 'formularesponse') ||          ($responsetype_per_response{$responsenum} eq 'formularesponse') ||
Line 6994  sub scantron_process_corrections { Line 8303  sub scantron_process_corrections {
  }   }
     }      }
     if ($err) {      if ($err) {
  $r->print(          $r->print(
             '<p class="LC_error">'              '<p class="LC_error">'
            .&mt('Unable to accept last correction, an error occurred: [_1]',             .&mt('Unable to accept last correction, an error occurred: [_1]',
                 $errmsg)                  $errmsg)
Line 7131  sub scantron_warning_screen { Line 8440  sub scantron_warning_screen {
             '<tr><td><b>'.&mt('Hand-graded items: points from last bubble in row').'</b></td><td><tt>'.              '<tr><td><b>'.&mt('Hand-graded items: points from last bubble in row').'</b></td><td><tt>'.
             $env{'form.scantron_lastbubblepoints'}.'</tt></td></tr>';              $env{'form.scantron_lastbubblepoints'}.'</tt></td></tr>';
     }      }
     return ('      return '
 <p>  <p>
 <span class="LC_warning">  <span class="LC_warning">
 '.&mt("Please double check the information below before clicking on '[_1]'",&mt($button_text)).'</span>  '.&mt("Please double check the information below before clicking on '[_1]'",&mt($button_text)).'</span>
Line 7143  sub scantron_warning_screen { Line 8452  sub scantron_warning_screen {
 </table>  </table>
 <p> '.&mt("If this information is correct, please click on '[_1]'.",&mt($button_text)).'<br />  <p> '.&mt("If this information is correct, please click on '[_1]'.",&mt($button_text)).'<br />
 '.&mt('If something is incorrect, please return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','<a href="/adm/grades?symb='.$symb.'&command=scantron_selectphase" class="LC_info">','</a>').'</p>  '.&mt('If something is incorrect, please return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','<a href="/adm/grades?symb='.$symb.'&command=scantron_selectphase" class="LC_info">','</a>').'</p>
   ';
 <br />  
 ');  
 }  }
   
 =pod  =pod
Line 7171  sub scantron_do_warning { Line 8478  sub scantron_do_warning {
  }    } 
  if ( $env{'form.scantron_selectfile'} eq '') {   if ( $env{'form.scantron_selectfile'} eq '') {
     $r->print('<p><span class="LC_error">'.&mt("You have not selected a file that contains the student's response data.").'</span></p>');      $r->print('<p><span class="LC_error">'.&mt("You have not selected a file that contains the student's response data.").'</span></p>');
  }    }
  if ( $env{'form.scantron_format'} eq '') {   if ( $env{'form.scantron_format'} eq '') {
     $r->print('<p><span class="LC_error">'.&mt("You have not selected the format of the student's response data.").'</span></p>');      $r->print('<p><span class="LC_error">'.&mt("You have not selected the format of the student's response data.").'</span></p>');
  }    }
     } else {      } else {
  my $warning=&scantron_warning_screen('Grading: Validate Records',$symb);   my $warning=&scantron_warning_screen('Grading: Validate Records',$symb);
           my ($checksec,@possibles) = &gradable_sections();
           my $gradesections;
           if ($checksec) {
               my $file=$env{'form.scantron_selectfile'};
               if (&valid_file($file)) {
                   my %bysec = &scantron_get_sections();
                   my $table;
                   if ((keys(%bysec) > 1) || ((keys(%bysec) == 1) && ((keys(%bysec))[0] ne $checksec))) {
                       $gradesections = &mt('Your current role is for section [_1].','<i>'.$checksec.'</i>').'<br />';
                       $table = &Apache::loncommon::start_data_table()."\n".
                                &Apache::loncommon::start_data_table_header_row().
                                '<th>'.&mt('Section').'</th><th>'.&mt('Number of records').'</th>'.
                                 &Apache::loncommon::end_data_table_header_row()."\n";
                       if ($bysec{'none'}) {
                           $table .= &Apache::loncommon::start_data_table_row().
                                     '<td>'.&mt('None').'</td><td>'.$bysec{'none'}.'</td>'.
                                     &Apache::loncommon::end_data_table_row()."\n";
                       }
                       foreach my $sec (sort { $a <=> $b } keys(%bysec)) {
                           next if ($sec eq 'none');
                           $table .= &Apache::loncommon::start_data_table_row().
                                     '<td>'.$sec.'</td><td>'.$bysec{$sec}.'</td>'.
                                     &Apache::loncommon::end_data_table_row()."\n";
                       }
                       $table .= &Apache::loncommon::end_data_table()."\n";
                       $gradesections .= &mt('Sections represented in the bubblesheet data file (based on bubbled student IDs) are as follows:').
                                         '<p>'.$table.'</p>';
                       if (@possibles) {
                           $gradesections .= '<p>'.
                                             &mt('You have role(s) in [quant,_1,other section,other sections] with privileges to manage grades.',
                                                 scalar(@possibles)).'<br />'.
                                             &mt('Check which of those section(s), in addition to section [_1], you wish to grade using this bubblesheet file:',
                                                 '<i>'.$checksec.'</i>').' ';
                           foreach my $sec (sort {$a <=> $b } @possibles) {
                               $gradesections .= '<label><input type="checkbox" name="scantron_othersections" value="'.$sec.'" />'.$sec.'</label>'.('&nbsp;'x2);
                           }
                           $gradesections .= '</p>';
                       }
                   }
               } else {
                   $gradesections = '<p class="LC_error">'.&mt('The selected file is unavailable').'</p>';
               }
           }
         my $bubbledbyhand=&hand_bubble_option();          my $bubbledbyhand=&hand_bubble_option();
  $r->print('   $r->print('
 '.$warning.$bubbledbyhand.'  '.$warning.$gradesections.$bubbledbyhand.'
 <input type="submit" name="submit" value="'.&mt('Grading: Validate Records').'" />  <input type="submit" name="submit" value="'.&mt('Grading: Validate Records').'" />
 <input type="hidden" name="command" value="scantron_validate" />  <input type="hidden" name="command" value="scantron_validate" />
 ');  ');
Line 7266  sub scantron_validate_file { Line 8616  sub scantron_validate_file {
     if ($env{'form.scantron_corrections'}) {      if ($env{'form.scantron_corrections'}) {
  &scantron_process_corrections($r);   &scantron_process_corrections($r);
     }      }
     $r->print('<p>'.&mt('Gathering necessary information.').'</p>');$r->rflush();  
       $r->print('<p>'.&mt('Gathering necessary information.').'</p>');
       my ($checksec,@gradable);
       if ($env{'request.course.sec'}) {
           ($checksec,my @possibles) = &gradable_sections();
           if ($checksec) {
               if (@possibles) {
                   my @chosensecs = &Apache::loncommon::get_env_multiple('form.scantron_othersections');
                   if (@chosensecs) {
                       foreach my $sec (@chosensecs) {
                           if (grep(/^\Q$sec\E$/,@possibles)) {
                               unless (grep(/^\Q$sec\E$/,@gradable)) {
                                   push(@gradable,$sec);
                               }
                           }
                       }
                   }
               }
               $r->print('<p><table>');
               if (@gradable) {
                   my @showsections = sort { $a <=> $b } (@gradable,$checksec);
                   $r->print(
                       '<tr><td><b>'.&mt('Sections to be Graded:').'</b></td><td>'.join(', ',@showsections).'</td></tr>');
               } else {
                   $r->print(
                       '<tr><td><b>'.&mt('Section to be Graded:').'</b></td><td>'.$checksec.'</td></tr>');
               }
               $r->print('</table></p>');
           }
       }
       $r->rflush();
   
     #get the student pick code ready      #get the student pick code ready
     $r->print(&Apache::loncommon::studentbrowser_javascript());      $r->print(&Apache::loncommon::studentbrowser_javascript());
     my $nav_error;      my $nav_error;
Line 7291  sub scantron_validate_file { Line 8672  sub scantron_validate_file {
  $env{'form.validatepass'} = 0;   $env{'form.validatepass'} = 0;
     }      }
     my $currentphase=$env{'form.validatepass'};      my $currentphase=$env{'form.validatepass'};
       my %skipbysec=();
   
     my $stop=0;      my $stop=0;
     while (!$stop && $currentphase < scalar(@validate_phases)) {      while (!$stop && $currentphase < scalar(@validate_phases)) {
  $r->print(&mt('Validating '.$validate_phases[$currentphase]).'<br />');   $r->print(&mt('Validating '.$validate_phases[$currentphase]).'<br />');
  $r->rflush();   $r->rflush();
        
  my $which="scantron_validate_".$validate_phases[$currentphase];   my $which="scantron_validate_".$validate_phases[$currentphase];
  {   {
     no strict 'refs';      no strict 'refs';
     ($stop,$currentphase)=&$which($r,$currentphase);              my @extras=();
               if ($validate_phases[$currentphase] eq 'ID') {
                   @extras = (\%skipbysec,$checksec,@gradable);
               }
       ($stop,$currentphase)=&$which($r,$currentphase,@extras);
  }   }
     }      }
     if (!$stop) {      if (!$stop) {
  my $warning=&scantron_warning_screen('Start Grading',$symb);   my $warning=&scantron_warning_screen('Start Grading',$symb);
           my $secinfo;
           if (keys(%skipbysec) > 0) {
               my $seclist = '<ul>';
               foreach my $sec (sort { $a <=> $b } keys(%skipbysec)) {
                   $seclist .= '<li>'.&mt('section [_1]: [_2]',$sec,$skipbysec{$sec}).'</li>';
               }
               $seclist .= '</ul>';
               $secinfo = '<p class="LC_info">'.
                          &mt('Numbers of records for students in sections not being graded [_1]',
                              $seclist).
                          '</p>';
           }
  $r->print(&mt('Validation process complete.').'<br />'.   $r->print(&mt('Validation process complete.').'<br />'.
                   $warning.                    $secinfo.$warning.
                   &mt('Perform verification for each student after storage of submissions?').                    &mt('Perform verification for each student after storage of submissions?').
                   '&nbsp;<span class="LC_nobreak"><label>'.                    '&nbsp;<span class="LC_nobreak"><label>'.
                   '<input type="radio" name="verifyrecord" value="1" />'.&mt('Yes').'</label>'.                    '<input type="radio" name="verifyrecord" value="1" />'.&mt('Yes').'</label>'.
Line 7327  sub scantron_validate_file { Line 8724  sub scantron_validate_file {
     $r->print('<input type="submit" name="submit" value="'.&mt('Ignore').' &rarr; " />');      $r->print('<input type="submit" name="submit" value="'.&mt('Ignore').' &rarr; " />');
     $r->print(' '.&mt('this error').' <br />');      $r->print(' '.&mt('this error').' <br />');
   
             $r->print('<p>'.&mt('Or return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','<a href="/adm/grades?symb='.$symb.'&command=scantron_selectphase" class="LC_info">','</a>').'</p>');      $r->print('<p>'.&mt('Or return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','<a href="/adm/grades?symb='.$symb.'&command=scantron_selectphase" class="LC_info">','</a>').'</p>');
  } else {   } else {
             if ($validate_phases[$currentphase] eq 'doublebubble' || $validate_phases[$currentphase] eq 'missingbubbles') {              if ($validate_phases[$currentphase] eq 'doublebubble' || $validate_phases[$currentphase] eq 'missingbubbles') {
         $r->print('<input type="button" name="submitbutton" value="'.&mt('Continue').' &rarr;" onclick="javascript:verify_bubble_radio(this.form)" />');          $r->print('<input type="button" name="submitbutton" value="'.&mt('Continue').' &rarr;" onclick="javascript:verify_bubble_radio(this.form)" />');
Line 7706  sub scantron_validate_sequence { Line 9103  sub scantron_validate_sequence {
  my @resources=   my @resources=
     $navmap->retrieveResources($map,\&scantron_filter_not_exam,1,0);      $navmap->retrieveResources($map,\&scantron_filter_not_exam,1,0);
  if (@resources) {   if (@resources) {
     $r->print('<p class="LC_warning">'      $r->print(
                   '<p class="LC_warning">'
                .&mt('Some resources in the sequence currently are not set to'                 .&mt('Some resources in the sequence currently are not set to'
                    .' exam mode. Grading these resources currently may not'                     .' bubblesheet exam mode. Grading these resources currently may not'
                    .' work correctly.')                     .' work correctly.')
                .'</p>'                 .'</p>'
             );              );
Line 7722  sub scantron_validate_sequence { Line 9120  sub scantron_validate_sequence {
   
   
 sub scantron_validate_ID {  sub scantron_validate_ID {
     my ($r,$currentphase) = @_;      my ($r,$currentphase,$skipbysec,$checksec,@gradable) = @_;
           
     #get student info      #get student info
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
       my $secidx = &Apache::loncoursedata::CL_SECTION();
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
Line 7740  sub scantron_validate_ID { Line 9139  sub scantron_validate_ID {
     }      }
   
     my %found=('ids'=>{},'usernames'=>{});      my %found=('ids'=>{},'usernames'=>{});
       my $unsavedskips = 0;
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {      for (my $i=0;$i<=$scanlines->{'count'};$i++) {
  my $line=&scantron_get_line($scanlines,$scan_data,$i);   my $line=&scantron_get_line($scanlines,$scan_data,$i);
  if ($line=~/^[\s\cz]*$/) { next; }   if ($line=~/^[\s\cz]*$/) { next; }
Line 7752  sub scantron_validate_ID { Line 9152  sub scantron_validate_ID {
  }   }
  if ($found) {   if ($found) {
     my $username=$idmap{$found};      my $username=$idmap{$found};
               if ($checksec) {
                   if (ref($classlist->{$username}) eq 'ARRAY') {
                       my $stusec = $classlist->{$username}->[$secidx];
                       if ($stusec ne $checksec) {
                           unless ((@gradable > 0) && (grep(/^\Q$stusec\E$/,@gradable))) {
                               my $skip=1;
                               &scantron_put_line($scanlines,$scan_data,$i,$line,$skip);
                               if (ref($skipbysec) eq 'HASH') {
                                   if ($stusec eq '') {
                                       $skipbysec->{'none'} ++;
                                   } else {
                                       $skipbysec->{$stusec} ++;
                                   }
                               }
                               $unsavedskips ++;
                               next;
                           }
                       }
                   }
               }
     if ($found{'ids'}{$found}) {      if ($found{'ids'}{$found}) {
  &scantron_get_correction($r,$i,$scan_record,\%scantron_config,   &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
  $line,'duplicateID',$found);   $line,'duplicateID',$found);
                   if ($unsavedskips) {
                       &scantron_putfile($scanlines,$scan_data);
                       $unsavedskips = 0;
                   }
  return(1,$currentphase);   return(1,$currentphase);
     } elsif ($found{'usernames'}{$username}) {      } elsif ($found{'usernames'}{$username}) {
  &scantron_get_correction($r,$i,$scan_record,\%scantron_config,   &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
  $line,'duplicateID',$username);   $line,'duplicateID',$username);
                   if ($unsavedskips) {
                       &scantron_putfile($scanlines,$scan_data);
                       $unsavedskips = 0;
                   }
  return(1,$currentphase);   return(1,$currentphase);
     }      }
     #FIXME store away line we previously saw the ID on to use above      #FIXME store away line we previously saw the ID on to use above
Line 7767  sub scantron_validate_ID { Line 9195  sub scantron_validate_ID {
  } else {   } else {
     if ($id =~ /^\s*$/) {      if ($id =~ /^\s*$/) {
  my $username=&scan_data($scan_data,"$i.user");   my $username=&scan_data($scan_data,"$i.user");
  if (defined($username) && $found{'usernames'}{$username}) {                  if (($checksec && $username ne '')) {
                       if (ref($classlist->{$username}) eq 'ARRAY') {
                           my $stusec = $classlist->{$username}->[$secidx];
                           if ($stusec ne $checksec) {
                               unless ((@gradable > 0) && (grep(/^\Q$stusec\E$/,@gradable))) {
                                   my $skip=1;
                                   &scantron_put_line($scanlines,$scan_data,$i,$line,$skip);
                                   if (ref($skipbysec) eq 'HASH') {
                                       if ($stusec eq '') {
                                           $skipbysec->{'none'} ++;
                                       } else {
                                           $skipbysec->{$stusec} ++;
                                       }
                                   }
                                   $unsavedskips ++;
                                   next;
                               }
                           }
                       }
    } elsif (defined($username) && $found{'usernames'}{$username}) {
     &scantron_get_correction($r,$i,$scan_record,      &scantron_get_correction($r,$i,$scan_record,
      \%scantron_config,       \%scantron_config,
      $line,'duplicateID',$username);       $line,'duplicateID',$username);
                       if ($unsavedskips) {
                           &scantron_putfile($scanlines,$scan_data);
                           $unsavedskips = 0;
                       }
     return(1,$currentphase);      return(1,$currentphase);
  } elsif (!defined($username)) {   } elsif (!defined($username)) {
     &scantron_get_correction($r,$i,$scan_record,      &scantron_get_correction($r,$i,$scan_record,
      \%scantron_config,       \%scantron_config,
      $line,'incorrectID');       $line,'incorrectID');
                       if ($unsavedskips) {
                           &scantron_putfile($scanlines,$scan_data);
                           $unsavedskips = 0;
                       }
     return(1,$currentphase);      return(1,$currentphase);
  }   }
  $found{'usernames'}{$username}++;   $found{'usernames'}{$username}++;
     } else {      } else {
  &scantron_get_correction($r,$i,$scan_record,\%scantron_config,   &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
  $line,'incorrectID');   $line,'incorrectID');
                   if ($unsavedskips) {
                       &scantron_putfile($scanlines,$scan_data);
                       $unsavedskips = 0;
                   }
  return(1,$currentphase);   return(1,$currentphase);
     }      }
  }   }
     }      }
       if ($unsavedskips) {
           &scantron_putfile($scanlines,$scan_data);
           $unsavedskips = 0;
       }
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
   sub scantron_get_sections {
       my %bysec;
       if ($env{'form.scantron_format'} ne '') {
           my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
           my ($scanlines,$scan_data)=&scantron_getfile();
           my $classlist=&Apache::loncoursedata::get_classlist();
           my %idmap=&username_to_idmap($classlist);
           foreach my $key (keys(%idmap)) {
               my $lckey = lc($key);
               $idmap{$lckey} = $idmap{$key};
           }
           my $secidx = &Apache::loncoursedata::CL_SECTION();
           for (my $i=0;$i<=$scanlines->{'count'};$i++) {
               my $line=&scantron_get_line($scanlines,$scan_data,$i);
               if ($line=~/^[\s\cz]*$/) { next; }
               my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
                                                        $scan_data);
               my $id=lc($$scan_record{'scantron.ID'});
               if (exists($idmap{$id})) {
                   if (ref($classlist->{$idmap{$id}}) eq 'ARRAY') {
                       my $stusec = $classlist->{$idmap{$id}}->[$secidx];
                       if ($stusec eq '') {
                           $bysec{'none'} ++;
                       } else {
                           $bysec{$stusec} ++;
                       }
                   }
               }
           }
       }
       return %bysec;
   }
   
 sub scantron_get_correction {  sub scantron_get_correction {
     my ($r,$i,$scan_record,$scan_config,$line,$error,$arg,      my ($r,$i,$scan_record,$scan_config,$line,$error,$arg,
Line 7830  sub scantron_get_correction { Line 9324  sub scantron_get_correction {
   
     if ($error =~ /ID$/) {      if ($error =~ /ID$/) {
  if ($error eq 'incorrectID') {   if ($error eq 'incorrectID') {
     $r->print('<p class="LC_warning">'.&mt("The encoded ID is not in the classlist").              $r->print('<p class="LC_warning">'.&mt("The encoded ID is not in the classlist").
       "</p>\n");        "</p>\n");
  } elsif ($error eq 'duplicateID') {   } elsif ($error eq 'duplicateID') {
     $r->print('<p class="LC_warning">'.&mt("The encoded ID has also been used by a previous paper [_1]",$arg)."</p>\n");              $r->print('<p class="LC_warning">'.&mt("The encoded ID has also been used by a previous paper [_1]",$arg)."</p>\n");
  }   }
  $r->print($message);   $r->print($message);
  $r->print("<p>".&mt("How should I handle this?")." <br /> \n");   $r->print("<p>".&mt("How should I handle this?")." <br /> \n");
Line 7853  sub scantron_get_correction { Line 9347  sub scantron_get_correction {
  } elsif ($error eq 'duplicateCODE') {   } elsif ($error eq 'duplicateCODE') {
     $r->print('<p class="LC_warning">'.&mt("The encoded CODE has also been used by a previous paper [_1], and CODEs are supposed to be unique.",join(', ',@{$arg}))."</p>\n");      $r->print('<p class="LC_warning">'.&mt("The encoded CODE has also been used by a previous paper [_1], and CODEs are supposed to be unique.",join(', ',@{$arg}))."</p>\n");
  }   }
         $r->print("<p>".&mt('The CODE on the form is [_1]',   $r->print("<p>".&mt('The CODE on the form is [_1]',
                             "<tt>'$$scan_record{'scantron.CODE'}'</tt>")      "<tt>'$$scan_record{'scantron.CODE'}'</tt>")
                  ."</p>\n");                   ."</p>\n");
  $r->print($message);   $r->print($message);
  $r->print("<p>".&mt("How should I handle this?")."</p>\n");   $r->print("<p>".&mt("How should I handle this?")."</p>\n");
Line 7951  ENDSCRIPT Line 9445  ENDSCRIPT
  # The form field scantron_questions is actually a list of line numbers not   # The form field scantron_questions is actually a list of line numbers not
  # a list of question numbers. Therefore:   # a list of question numbers. Therefore:
  #   #
   
  my $line_list = &questions_to_line_list($arg,$randomorder,$randompick,   my $line_list = &questions_to_line_list($arg,$randomorder,$randompick,
                                                 $respnumlookup,$startline);                                                  $respnumlookup,$startline);
   
Line 8061  sub questions_to_line_list { Line 9555  sub questions_to_line_list {
             } else {              } else {
                 $first = $first_bubble_line{$responsenum} + 1;                  $first = $first_bubble_line{$responsenum} + 1;
             }              }
             $count   = $bubble_lines_per_response{$responsenum};      $count   = $bubble_lines_per_response{$responsenum};
         }          }
         $last = $first+$count-1;          $last = $first+$count-1;
         push(@lines, ($first..$last));          push(@lines, ($first..$last));
Line 8092  for multi and missing bubble cases). Line 9586  for multi and missing bubble cases).
                   and value is number of first bubble line for current student                    and value is number of first bubble line for current student
                   or code-based randompick and/or randomorder.                    or code-based randompick and/or randomorder.
   
   
  Implicit inputs:   Implicit inputs:
    %bubble_lines_per_response   - Starting line numbers for each question.     %bubble_lines_per_response   - Starting line numbers for each question.
                                   Numbered from 0 (but question numbers are from                                    Numbered from 0 (but question numbers are from
Line 8144  sub prompt_for_corrections { Line 9639  sub prompt_for_corrections {
     } else {      } else {
         if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH')) {          if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH')) {
             $responsenum = $respnumlookup->{$question-1};              $responsenum = $respnumlookup->{$question-1};
             if (ref($startline) eq 'HASH') {              if (ref($startline) eq 'HASH') { 
                 $first = $startline->{$question-1};                  $first = $startline->{$question-1};
             }              }
         } else {          } else {
Line 8162  sub prompt_for_corrections { Line 9657  sub prompt_for_corrections {
             ($responsetype_per_response{$responsenum} eq 'imageresponse') ||              ($responsetype_per_response{$responsenum} eq 'imageresponse') ||
             ($responsetype_per_response{$responsenum} eq 'reactionresponse') ||              ($responsetype_per_response{$responsenum} eq 'reactionresponse') ||
             ($responsetype_per_response{$responsenum} eq 'organicresponse')) {              ($responsetype_per_response{$responsenum} eq 'organicresponse')) {
             $r->print(&mt("Although this particular question type requires handgrading, the instructions for this question in the bubblesheet exam directed students to leave [quant,_1,line] blank on their bubblesheets.",$lines).'<br /><br />'.&mt('A non-zero score can be assigned to the student during bubblesheet grading by selecting a bubble in at least one line.').'<br />'.&mt('The score for this question will be a sum of the numeric values for the selected bubbles from each line, where A=1 point, B=2 points etc.').'<br />'.&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.").'<br /><br />');              $r->print(
                   &mt("Although this particular question type requires handgrading, the instructions for this question in the bubblesheet exam directed students to leave [quant,_1,line] blank on their bubblesheets.",$lines)
                  .'<br /><br />'
                  .&mt('A non-zero score can be assigned to the student during bubblesheet grading by selecting a bubble in at least one line.')
                  .'<br />'
                  .&mt('The score for this question will be a sum of the numeric values for the selected bubbles from each line, where A=1 point, B=2 points etc.')
                  .'<br />'
                  .&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.")
                  .'<br /><br />'
               );
         } else {          } else {
             $r->print(&mt("Select at most one bubble in a single line and select 'No Bubble' in all the other lines. ")."<br />");              $r->print(&mt("Select at most one bubble in a single line and select 'No Bubble' in all the other lines. ")."<br />");
         }          }
Line 8202  sub scantron_bubble_selector { Line 9706  sub scantron_bubble_selector {
     my $max=$$scan_config{'Qlength'};      my $max=$$scan_config{'Qlength'};
   
     my $scmode=$$scan_config{'Qon'};      my $scmode=$$scan_config{'Qon'};
     if ($scmode eq 'number' || $scmode eq 'letter') {      if ($scmode eq 'number' || $scmode eq 'letter') { 
         if (($$scan_config{'BubblesPerRow'} =~ /^\d+$/) &&          if (($$scan_config{'BubblesPerRow'} =~ /^\d+$/) &&
             ($$scan_config{'BubblesPerRow'} > 0)) {              ($$scan_config{'BubblesPerRow'} > 0)) {
             $max=$$scan_config{'BubblesPerRow'};              $max=$$scan_config{'BubblesPerRow'};
Line 8445  sub scantron_validate_doublebubble { Line 9949  sub scantron_validate_doublebubble {
     if (ref($map)) {      if (ref($map)) {
         $randomorder = $map->randomorder();          $randomorder = $map->randomorder();
         $randompick = $map->randompick();          $randompick = $map->randompick();
           unless ($randomorder || $randompick) {
               foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
                   if ($res->randomorder()) {
                       $randomorder = 1;
                   }
                   if ($res->randompick()) {
                       $randompick = 1;
                   }
                   last if ($randomorder || $randompick);
               }
           }
         if ($randomorder || $randompick) {          if ($randomorder || $randompick) {
             $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);              $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
             if ($nav_error) {              if ($nav_error) {
Line 8521  sub scantron_get_maxbubble { Line 10036  sub scantron_get_maxbubble {
     my $response_number = 0;      my $response_number = 0;
     my $bubble_line     = 0;      my $bubble_line     = 0;
     foreach my $resource (@resources) {      foreach my $resource (@resources) {
         my $resid = $resource->id();          my $resid = $resource->id(); 
         my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,          my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,
                                                           $udom,undef,$bubbles_per_row);                                                            $udom,undef,$bubbles_per_row);
         if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) {          if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) {
Line 8572  sub scantron_get_maxbubble { Line 10087  sub scantron_get_maxbubble {
         $bubble_lines_per_response{$response_number} = $lines;          $bubble_lines_per_response{$response_number} = $lines;
                 $responsetype_per_response{$response_number} =                   $responsetype_per_response{$response_number} = 
                     $analysis->{$part_id.'.type'};                      $analysis->{$part_id.'.type'};
                 $masterseq_id_responsenum{$resid.'_'.$part_id} = $response_number;                  $masterseq_id_responsenum{$resid.'_'.$part_id} = $response_number;  
         $response_number++;          $response_number++;
   
         $bubble_line +=  $lines;          $bubble_line +=  $lines;
Line 8628  sub scantron_validate_missingbubbles { Line 10143  sub scantron_validate_missingbubbles {
     if (ref($map)) {      if (ref($map)) {
         $randomorder = $map->randomorder();          $randomorder = $map->randomorder();
         $randompick = $map->randompick();          $randompick = $map->randompick();
           unless ($randomorder || $randompick) {
               foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
                   if ($res->randomorder()) {
                       $randomorder = 1;
                   }
                   if ($res->randompick()) {
                       $randompick = 1;
                   }
                   last if ($randomorder || $randompick);
               }
           }
         if ($randomorder || $randompick) {          if ($randomorder || $randompick) {
             $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);              $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
             if ($nav_error) {              if ($nav_error) {
Line 8653  sub scantron_validate_missingbubbles { Line 10179  sub scantron_validate_missingbubbles {
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {      for (my $i=0;$i<=$scanlines->{'count'};$i++) {
  my $line=&scantron_get_line($scanlines,$scan_data,$i);   my $line=&scantron_get_line($scanlines,$scan_data,$i);
  if ($line=~/^[\s\cz]*$/) { next; }   if ($line=~/^[\s\cz]*$/) { next; }
         my $scan_record =   my $scan_record =
             &scantron_parse_scanline($line,$i,\%scantron_config,$scan_data,undef,\%idmap,              &scantron_parse_scanline($line,$i,\%scantron_config,$scan_data,undef,\%idmap,
                                      $randomorder,$randompick,$sequence,\@master_seq,       $randomorder,$randompick,$sequence,\@master_seq,
                                      \%symb_to_resource,\%grader_partids_by_symb,                                       \%symb_to_resource,\%grader_partids_by_symb,
                                      \%orderedforcode,\%respnumlookup,\%startline);                                       \%orderedforcode,\%respnumlookup,\%startline);
  if (!defined($$scan_record{'scantron.missingerror'})) { next; }   if (!defined($$scan_record{'scantron.missingerror'})) { next; }
Line 8666  sub scantron_validate_missingbubbles { Line 10192  sub scantron_validate_missingbubbles {
  foreach my $missing (@{$$scan_record{'scantron.missingerror'}}) {   foreach my $missing (@{$$scan_record{'scantron.missingerror'}}) {
             my $lastbubble;              my $lastbubble;
             if ($missing =~ /^(\d+)\.(\d+)$/) {              if ($missing =~ /^(\d+)\.(\d+)$/) {
                 my $question = $1;                 my $question = $1;
                 my $subquestion = $2;                 my $subquestion = $2;
                 my ($first,$responsenum);                 my ($first,$responsenum);
                 if ($randomorder || $randompick) {                 if ($randomorder || $randompick) {
                     $responsenum = $respnumlookup{$question-1};                     $responsenum = $respnumlookup{$question-1};
                     $first = $startline{$question-1};                     $first = $startline{$question-1};
                 } else {                 } else {
                     $responsenum = $question-1;                     $responsenum = $question-1; 
                     $first = $first_bubble_line{$responsenum};                     $first = $first_bubble_line{$responsenum};
                 }                 }
                 if (!defined($first)) { next; }                 if (!defined($first)) { next; }
                 my @subans = split(/,/,$subdivided_bubble_lines{$responsenum});                 my @subans = split(/,/,$subdivided_bubble_lines{$responsenum});
                 my $subcount = 1;                 my $subcount = 1;
                 while ($subcount<$subquestion) {                 while ($subcount<$subquestion) {
                     $first += $subans[$subcount-1];                     $first += $subans[$subcount-1];
                     $subcount ++;                     $subcount ++;
                 }                 }
                 my $count = $subans[$subquestion-1];                 my $count = $subans[$subquestion-1];
                 $lastbubble = $first + $count;                 $lastbubble = $first + $count;
             } else {              } else {
                 my ($first,$responsenum);                 my ($first,$responsenum);
                 if ($randomorder || $randompick) {                 if ($randomorder || $randompick) {
                     $responsenum = $respnumlookup{$missing-1};                     $responsenum = $respnumlookup{$missing-1};
                     $first = $startline{$missing-1};                     $first = $startline{$missing-1};
                 } else {                 } else {
                     $responsenum = $missing-1;                     $responsenum = $missing-1;
                     $first = $first_bubble_line{$responsenum};                     $first = $first_bubble_line{$responsenum};
                 }                 }
                 if (!defined($first)) { next; }                 if (!defined($first)) { next; }
                 $lastbubble = $first + $bubble_lines_per_response{$responsenum};                 $lastbubble = $first + $bubble_lines_per_response{$responsenum};
             }              }
             if ($lastbubble > $max_bubble) { next; }              if ($lastbubble > $max_bubble) { next; }
     push(@to_correct,$missing);      push(@to_correct,$missing);
Line 8758  sub scantron_process_students { Line 10284  sub scantron_process_students {
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
   
     my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);      my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config); 
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
Line 8769  sub scantron_process_students { Line 10295  sub scantron_process_students {
     }      }
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,      my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
         %grader_randomlists_by_symb);          %grader_randomlists_by_symb,%symb_for_examcode);
     if (ref($map)) {      if (ref($map)) {
         $randomorder = $map->randomorder();          $randomorder = $map->randomorder();
         $randompick = $map->randompick();          $randompick = $map->randompick();
           unless ($randomorder || $randompick) {
               foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
                   if ($res->randomorder()) {
                       $randomorder = 1;
                   }
                   if ($res->randompick()) {
                       $randompick = 1;
                   }
                   last if ($randomorder || $randompick);
               }
           }
     } else {      } else {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return '';          return '';
Line 8780  sub scantron_process_students { Line 10317  sub scantron_process_students {
     my $nav_error;      my $nav_error;
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
     if ($randomorder || $randompick) {      if ($randomorder || $randompick) {
         $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);          $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource,1,\%symb_for_examcode);
         if ($nav_error) {          if ($nav_error) {
             $r->print(&navmap_errormsg());              $r->print(&navmap_errormsg());
             return '';              return '';
Line 8797  sub scantron_process_students { Line 10334  sub scantron_process_students {
 SCANTRONFORM  SCANTRONFORM
     $r->print($result);      $r->print($result);
   
       my ($checksec,@possibles)=&gradable_sections();
     my @delayqueue;      my @delayqueue;
     my (%completedstudents,%scandata);      my (%completedstudents,%scandata);
       
     my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));      my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));
     my $count=&get_todo_count($scanlines,$scan_data);      my $count=&get_todo_count($scanlines,$scan_data);
     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);
Line 8842  SCANTRONFORM Line 10380  SCANTRONFORM
         my %startline = ();          my %startline = ();
         my $total;          my $total;
   my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,    my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
   $scan_data,undef,\%idmap,$randomorder,                                                   $scan_data,undef,\%idmap,$randomorder,
                                                  $randompick,$sequence,\@master_seq,                                                   $randompick,$sequence,\@master_seq,
                                                  \%symb_to_resource,\%grader_partids_by_symb,                                                   \%symb_to_resource,\%grader_partids_by_symb,
                                                  \%orderedforcode,\%respnumlookup,\%startline,                                                   \%orderedforcode,\%respnumlookup,\%startline,
Line 8859  SCANTRONFORM Line 10397  SCANTRONFORM
      next;       next;
   }    }
         my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION];          my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION];
           if (($checksec ne '') && ($checksec ne $usec)) {
               unless (grep(/^\Q$usec\E$/,@possibles)) {
                   &scantron_add_delay(\@delayqueue,$line,
                                       "No role with manage grades privilege in student's section ($usec)",3);
                   next;
               }
           }
         my $user = $uname.':'.$usec;          my $user = $uname.':'.$usec;
   ($uname,$udom)=split(/:/,$uname);    ($uname,$udom)=split(/:/,$uname);
   
Line 8872  SCANTRONFORM Line 10417  SCANTRONFORM
   
         my @mapresources = @resources;          my @mapresources = @resources;
         if ($randomorder || $randompick) {          if ($randomorder || $randompick) {
             @mapresources =              @mapresources = 
                 &users_order($user,$scancode,$sequence,\@master_seq,\%symb_to_resource,                  &users_order($user,$scancode,$sequence,\@master_seq,\%symb_to_resource,
                              \%orderedforcode);                               \%orderedforcode);
         }          }
Line 8927  SCANTRONFORM Line 10472  SCANTRONFORM
         }          }
   
         if (($scancode) && ($randomorder || $randompick)) {          if (($scancode) && ($randomorder || $randompick)) {
             my $parmresult =              foreach my $key (keys(%symb_for_examcode)) {
                 &Apache::lonparmset::storeparm_by_symb($symb,                  my $symb_in_map = $symb_for_examcode{$key};
                                                        '0_examcode',2,$scancode,                  if ($symb_in_map ne '') {
                                                        'string_examcode',$uname,                      my $parmresult =
                                                        $udom);                          &Apache::lonparmset::storeparm_by_symb($symb_in_map,
                                                                  '0_examcode',2,$scancode,
                                                                  'string_examcode',$uname,
                                                                  $udom);
                   }
               }
         }          }
  $completedstudents{$uname}={'line'=>$line};   $completedstudents{$uname}={'line'=>$line};
         if ($env{'form.verifyrecord'}) {          if ($env{'form.verifyrecord'}) {
Line 8961  SCANTRONFORM Line 10511  SCANTRONFORM
                 if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,                  if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
                                            \@mapresources,\%partids_by_symb,                                             \@mapresources,\%partids_by_symb,
                                            $bubbles_per_row,$randomorder,$randompick,                                             $bubbles_per_row,$randomorder,$randompick,
                                            \%respnumlookup,\%startline)                                             \%respnumlookup,\%startline) 
                     eq 'ssi_error') {                      eq 'ssi_error') {
                     $ssi_error = 0; # So end of handler error message does not trigger.                      $ssi_error = 0; # So end of handler error message does not trigger.
                     $r->print("</form>");                      $r->print("</form>");
Line 9054  sub graders_resources_pass { Line 10604  sub graders_resources_pass {
 =item users_order  =item users_order
   
   Returns array of resources in current map, ordered based on either CODE,    Returns array of resources in current map, ordered based on either CODE,
   if this is a CODEd exam, or based on student's identity if this is a    if this is a CODEd exam, or based on student's identity if this is a 
   "NAMEd" exam.    "NAMEd" exam.
   
   Should be used when randomorder and/or randompick applied when the     Should be used when randomorder and/or randompick applied when the 
Line 9081  sub users_order  { Line 10631  sub users_order  {
             if (ref($actual_seq) eq 'ARRAY') {              if (ref($actual_seq) eq 'ARRAY') {
                 @mapresources = map { $symb_to_resource->{$_}; } @{$actual_seq};                  @mapresources = map { $symb_to_resource->{$_}; } @{$actual_seq};
                 if (ref($orderedforcode) eq 'HASH') {                  if (ref($orderedforcode) eq 'HASH') {
                     if (@mapresources > 0) {                      if (@mapresources > 0) { 
                         $orderedforcode->{$scancode} = \@mapresources;                          $orderedforcode->{$scancode} = \@mapresources;
                     }                      }
                 }                  }
Line 9094  sub users_order  { Line 10644  sub users_order  {
                                                            $master_seq,                                                             $master_seq,
                                                            $user,undef,1);                                                             $user,undef,1);
         if (ref($actual_seq) eq 'ARRAY') {          if (ref($actual_seq) eq 'ARRAY') {
             @mapresources =              @mapresources = 
                 map { $symb_to_resource->{$_}; } @{$actual_seq};                  map { $symb_to_resource->{$_}; } @{$actual_seq};
         }          }
     }      }
Line 9159  sub scantron_upload_scantron_data { Line 10709  sub scantron_upload_scantron_data {
   'domainid',    'domainid',
   'coursename',$dom);    'coursename',$dom);
     my $syllabuslink = '<a href="javascript:ToSyllabus();">'.&mt('Syllabus').'</a>'.      my $syllabuslink = '<a href="javascript:ToSyllabus();">'.&mt('Syllabus').'</a>'.
                        ('&nbsp'x2).&mt('(shows course personnel)');                         ('&nbsp'x2).&mt('(shows course personnel)'); 
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
     my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.');      my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.');
     &js_escape(\$nofile_alert);      &js_escape(\$nofile_alert);
Line 9240  END Line 10790  END
             if (keys(%{$domconfig{'scantron'}{'config'}}) > 1) {              if (keys(%{$domconfig{'scantron'}{'config'}}) > 1) {
                 if (($domconfig{'scantron'}{'config'}{'dat'}) &&                  if (($domconfig{'scantron'}{'config'}{'dat'}) &&
                     (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH')) {                      (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH')) {
                     if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {                      if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {  
                         if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {                          if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {
                             my ($onclick,$formatextra,$singleline);                              my ($onclick,$formatextra,$singleline);
                             my @lines = &Apache::lonnet::get_scantronformat_file();                              my @lines = &Apache::lonnet::get_scantronformat_file();
                             my $count = 0;                              my $count = 0;
                             foreach my $line (@lines) {                              foreach my $line (@lines) {
                                 next if ($line =~ /^#/);                                  next if (($line =~ /^\#/) || ($line eq ''));
                                 $singleline = $line;                                  $singleline = $line;
                                 $count ++;                                  $count ++;
                             }                              }
Line 9314  sub scantron_upload_scantron_data_save { Line 10864  sub scantron_upload_scantron_data_save {
  '</form>'."\n";   '</form>'."\n";
     if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&      if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&
  !&Apache::lonnet::allowed('usc',   !&Apache::lonnet::allowed('usc',
     $env{'form.domainid'}.'_'.$env{'form.courseid'})) {      $env{'form.domainid'}.'_'.$env{'form.courseid'}) &&
           !&Apache::lonnet::allowed('usc',
                               $env{'form.domainid'}.'_'.$env{'form.courseid'}.'/'.$env{'form.coursesec'})) {
  $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."<br />");   $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."<br />");
         unless ($symb) {   unless ($symb) {
     $r->print($doanotherupload);      $r->print($doanotherupload);
  }   }
  return '';   return '';
     }      }
     my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});      my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});
     my $uploadedfile;      my $uploadedfile;
     $r->print('<p>'.&mt("Uploading file to [_1]",$coursedata{'description'}).'</p>');      $r->print('<p>'.&mt('Uploading file to [_1]','"'.$coursedata{'description'}.'"').'</p>');
     if (length($env{'form.upfile'}) < 2) {      if (length($env{'form.upfile'}) < 2) {
         $r->print(          $r->print(
             &Apache::lonhtmlcommon::confirm_success(              &Apache::lonhtmlcommon::confirm_success(
Line 9363  sub scantron_upload_scantron_data_save { Line 10915  sub scantron_upload_scantron_data_save {
         my $result =          my $result =
             &Apache::lonnet::userfileupload('upfile','scantron','scantron',$parser,'','',              &Apache::lonnet::userfileupload('upfile','scantron','scantron',$parser,'','',
                                             $env{'form.courseid'},$env{'form.domainid'});                                              $env{'form.courseid'},$env{'form.domainid'});
  if ($result =~ m{^/uploaded/}) {          if ($result =~ m{^/uploaded/}) {
             $r->print(              $r->print(
                 &Apache::lonhtmlcommon::confirm_success(&mt('Upload successful')).'<br />'.                  &Apache::lonhtmlcommon::confirm_success(&mt('Upload successful')).'<br />'.
                 &mt('Uploaded [_1] bytes of data into location: [_2]',                  &mt('Uploaded [_1] bytes of data into location: [_2]',
                         (length($env{'form.upfile'})-1),                          (length($env{'form.upfile'})-1),
                         '<span class="LC_filename">'.$result.'</span>'));                          '<span class="LC_filename">'.$result.'</span>'));
             ($uploadedfile) = ($result =~ m{/([^/]+)$});              ($uploadedfile) = ($result =~ m{/([^/]+)$});
               if ($uploadedfile =~ /^scantron_orig_/) {
                   my $logname = $uploadedfile;
                   $logname =~ s/^scantron_orig_//;
                   if ($logname ne '') {
                       my $now = time;
                       my %info = ($logname => { $now => $env{'user.name'}.':'.$env{'user.domain'} });  
                       &Apache::lonnet::put('scantronupload',\%info,$env{'form.domainid'},$env{'form.courseid'});
                   }
               }
             $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},              $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},
                                                        $env{'form.courseid'},$uploadedfile));                                                         $env{'form.courseid'},$symb,$uploadedfile));
  } else {          } else {
             $r->print(              $r->print(
                 &Apache::lonhtmlcommon::confirm_success(&mt('Upload failed'),1).'<br />'.                  &Apache::lonhtmlcommon::confirm_success(&mt('Upload failed'),1).'<br />'.
                     &mt('An error ([_1]) occurred when attempting to upload the file: [_2]',                      &mt('An error ([_1]) occurred when attempting to upload the file: [_2]',
Line 9389  sub scantron_upload_scantron_data_save { Line 10950  sub scantron_upload_scantron_data_save {
 }  }
   
 sub validate_uploaded_scantron_file {  sub validate_uploaded_scantron_file {
     my ($cdom,$cname,$fname) = @_;      my ($cdom,$cname,$symb,$fname,$context,$countsref) = @_;
   
     my $scanlines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.$fname);      my $scanlines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.$fname);
     my @lines;      my @lines;
     if ($scanlines ne '-1') {      if ($scanlines ne '-1') {
         @lines=split("\n",$scanlines,-1);          @lines=split("\n",$scanlines,-1);
     }      }
     my $output;      my ($output,$secidx,$checksec,$priv,%crsroleshash,@possibles);
       $secidx = &Apache::loncoursedata::CL_SECTION();
       if ($context eq 'download') {
           $priv = 'mgr';
       } else {
           $priv = 'usc';
       }
       unless ((&Apache::lonnet::allowed($priv,$env{'request.role.domain'})) ||
               (($env{'request.course.id'}) &&
                (&Apache::lonnet::allowed($priv,$env{'request.course.id'})))) {
           if ($env{'request.course.sec'} ne '') {
               unless (&Apache::lonnet::allowed($priv,
                                            "$env{'request.course.id'}/$env{'request.course.sec'}")) {
                   unless ($context eq 'download') {
                       $output = '<p class="LC_warning">'.&mt('You do not have permission to upload bubblesheet data').'</p>';
                   }
                   return $output;
               }
               ($checksec,@possibles)=&gradable_sections();
           }
       }
     if (@lines) {      if (@lines) {
         my (%counts,$max_match_format);          my (%counts,$max_match_format);
         my ($found_match_count,$max_match_count,$max_match_pct) = (0,0,0);          my ($found_match_count,$max_match_count,$max_match_pct) = (0,0,0);
Line 9408  sub validate_uploaded_scantron_file { Line 10990  sub validate_uploaded_scantron_file {
         my %unique_formats;          my %unique_formats;
         my @formatlines = &Apache::lonnet::get_scantronformat_file();          my @formatlines = &Apache::lonnet::get_scantronformat_file();
         foreach my $line (@formatlines) {          foreach my $line (@formatlines) {
             chomp($line);              next if (($line =~ /^\#/) || ($line eq ''));
             my @config = split(/:/,$line);              my @config = split(/:/,$line);
             my $idstart = $config[5];              my $idstart = $config[5];
             my $idlength = $config[6];              my $idlength = $config[6];
Line 9425  sub validate_uploaded_scantron_file { Line 11007  sub validate_uploaded_scantron_file {
             %{$counts{$key}} = (              %{$counts{$key}} = (
                                'found'   => 0,                                 'found'   => 0,
                                'total'   => 0,                                 'total'   => 0,
                                  'totalanysec' => 0,
                                  'othersec' => 0,
                               );                                );
             foreach my $line (@lines) {              foreach my $line (@lines) {
                 next if ($line =~ /^#/);                  next if ($line =~ /^#/);
Line 9432  sub validate_uploaded_scantron_file { Line 11016  sub validate_uploaded_scantron_file {
                 my $id = substr($line,$idstart-1,$idlength);                  my $id = substr($line,$idstart-1,$idlength);
                 $id = lc($id);                  $id = lc($id);
                 if (exists($idmap{$id})) {                  if (exists($idmap{$id})) {
                       if ($checksec ne '') {
                           $counts{$key}{'totalanysec'} ++;
                           if (ref($classlist->{$idmap{$id}}) eq 'ARRAY') {
                               my $stusec = $classlist->{$idmap{$id}}->[$secidx];
                               if ($stusec ne $checksec) {
                                   if (@possibles) {
                                       unless (grep(/^\Q$stusec\E$/,@possibles)) {
                                           $counts{$key}{'othersec'} ++;
                                           next;
                                       }
                                   } else {
                                       $counts{$key}{'othersec'} ++;
                                       next;
                                   }
                               }
                           }
                       }
                     $counts{$key}{'found'} ++;                      $counts{$key}{'found'} ++;
                 }                  }
                 $counts{$key}{'total'} ++;                  $counts{$key}{'total'} ++;
Line 9446  sub validate_uploaded_scantron_file { Line 11047  sub validate_uploaded_scantron_file {
                 }                  }
             }              }
         }          }
         if (ref($unique_formats{$max_match_format}) eq 'ARRAY') {          if ((ref($unique_formats{$max_match_format}) eq 'ARRAY') && ($context ne 'download')) {
             my $format_descs;              my $format_descs;
             my $numwithformat = @{$unique_formats{$max_match_format}};              my $numwithformat = @{$unique_formats{$max_match_format}};
             for (my $i=0; $i<$numwithformat; $i++) {              for (my $i=0; $i<$numwithformat; $i++) {
Line 9491  sub validate_uploaded_scantron_file { Line 11092  sub validate_uploaded_scantron_file {
                     '<li>'.&mt('The course roster is not up to date.').'</li>'.                      '<li>'.&mt('The course roster is not up to date.').'</li>'.
                     '</ul>';                      '</ul>';
             }              }
               if (($checksec ne '') && (ref($counts{$max_match_format}) eq 'HASH')) {
                   if ($counts{$max_match_format}{'othersec'}) {
                       my $percent_nongrade = (100*$counts{$max_match_format}{'othersec'})/($counts{$max_match_format}{'totalanysec'});
                       my $showpct = sprintf("%.0f",$percent_nongrade).'%';
                       my $confirmdel = &mt('Are you sure you want to permanently delete this file?');
                       &js_escape(\$confirmdel);
                       $output .= '<p class="LC_warning">'.
                                  &mt('Comparison of student IDs in the uploaded file with the course roster found [_1][quant,_2,match,matches][_3] for students in section(s) for which none of your role(s) have privileges to modify grades',
                                      '<b>',$counts{$max_match_format}{'othersec'},'</b>').
                                  '<br />'.
                                  &mt('Unless you are assigned role(s) which allow modification of grades in additional sections, [_1] of the records in this file will be automatically excluded when you perform bubblesheet grading.','<b>'.$showpct.'</b>').
                                  '</p><p>'.
                                  &mt('If you prefer to delete the file now, use: [_1]').
                                  '<form method="post" name="delupload" action="/adm/grades">'.
                                  '<input type="hidden" name="symb" value="'.$symb.'" />'.
                                  '<input type="hidden" name="domainid" value="'.$cdom.'" />'.
                                  '<input type="hidden" name="courseid" value="'.$cname.'" />'.
                                  '<input type="hidden" name="coursesec" value="'.$env{'request.course.sec'}.'" />'. 
                                  '<input type="hidden" name="uploadedfile" value="'.$fname.'" />'. 
                                  '<input type="hidden" name="command" value="scantronupload_delete" />'.
                                  '<input type="button" name="delbutton" value="'.&mt('Delete Uploaded File').'" onclick="javascript:if (confirm('."'$confirmdel'".')) { document.delupload.submit(); }" />'.
                                  '</form></p>';
                   }
               }
         }          }
     } else {          if (($context eq 'download') && ($checksec ne '')) {
               if ((ref($countsref) eq 'HASH') && (ref($counts{$max_match_format}) eq 'HASH')) {
                   $countsref->{'totalanysec'} = $counts{$max_match_format}{'totalanysec'};
                   $countsref->{'othersec'} = $counts{$max_match_format}{'othersec'};
               }
           } 
       } elsif ($context ne 'download') {
         $output = '<p class="LC_warning">'.&mt('Uploaded file contained no data').'</p>';          $output = '<p class="LC_warning">'.&mt('Uploaded file contained no data').'</p>';
     }      }
     return $output;      return $output;
 }  }
   
   sub gradable_sections {
       my $checksec = $env{'request.course.sec'};
       my @oksecs;
       if ($checksec) {
           my %availablesecs = &sections_grade_privs();
           if (ref($availablesecs{'mgr'}) eq 'ARRAY') {
               foreach my $sec (@{$availablesecs{'mgr'}}) {
                   unless (grep(/^\Q$sec\E$/,@oksecs)) {
                       push(@oksecs,$sec);
                   }
               }
               if (grep(/^all$/,@oksecs)) {
                   undef($checksec);
               }
           }
       }
       return($checksec,@oksecs);
   }
   
   sub sections_grade_privs {
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
       my %availablesecs = (
                             mgr => [],
                             vgr => [],
                             usc => [],
                           );
       my $ccrole = 'cc';
       if ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Community') {
           $ccrole = 'co';
       }
       my %crsroleshash = &Apache::lonnet::get_my_roles($env{'user.name'},$env{'user.domain'},
                                                        'userroles',['active'],
                                                        [$ccrole,'in','cr'],$cdom,1);
       my $crsid = $cnum.':'.$cdom;
       foreach my $item (keys(%crsroleshash)) {
           next unless ($item =~ /^$crsid\:/);
           my ($crsnum,$crsdom,$role,$sec) = split(/\:/,$item);
           my $suffix = "/$cdom/$cnum./$cdom/$cnum";
           if ($sec ne '') {
               $suffix = "/$cdom/$cnum/$sec./$cdom/$cnum/$sec";
           }
           if (($role eq $ccrole) || ($role eq 'in')) {
               foreach my $priv ('mgr','vgr','usc') { 
                   unless (grep(/^all$/,@{$availablesecs{$priv}})) {
                       if ($sec eq '') {
                           $availablesecs{$priv} = ['all'];
                       } elsif ($sec ne $env{'request.course.sec'}) {
                           unless (grep(/^\Q$sec\E$/,@{$availablesecs{$priv}})) {
                               push(@{$availablesecs{$priv}},$sec);
                           }
                       }
                   }
               }
           } elsif ($role =~ m{^cr/}) {
               foreach my $priv ('mgr','vgr','usc') {
                   unless (grep(/^all$/,@{$availablesecs{$priv}})) {
                       if ($env{"user.priv.$role.$suffix"} =~ /:$priv&/) {
                           if ($sec eq '') {
                               $availablesecs{$priv} = ['all'];
                           } elsif ($sec ne $env{'request.course.sec'}) {
                               unless (grep(/^\Q$sec\E$/,@{$availablesecs{$priv}})) {
                                   push(@{$availablesecs{$priv}},$sec);
                               }
                           }
                       }
                   }
               }
           }
       }
       return %availablesecs;
   }
   
   sub scantron_upload_delete {
       my ($r,$symb) = @_;
       my $filename = $env{'form.uploadedfile'};
       if ($filename =~ /^scantron_orig_/) {
           if (&Apache::lonnet::allowed('usc',$env{'form.domainid'}) ||
               &Apache::lonnet::allowed('usc',
                                        $env{'form.domainid'}.'_'.$env{'form.courseid'}) ||
               &Apache::lonnet::allowed('usc',
                                        $env{'form.domainid'}.'_'.$env{'form.courseid'}.'/'.$env{'form.coursesec'})) {
               my $uploadurl = '/uploaded/'.$env{'form.domainid'}.'/'.$env{'form.courseid'}.'/'.$env{'form.uploadedfile'};
               my $retrieval = &Apache::lonnet::getfile($uploadurl);
               if ($retrieval eq '-1') {
                   $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                             &mt('File requested for deletion not found.'));
               } else {
                   $filename =~ s/^scantron_orig_//;
                   if ($filename ne '') {
                       my ($is_valid,$numleft);
                       my %info = &Apache::lonnet::get('scantronupload',[$filename],$env{'form.domainid'},$env{'form.courseid'});
                       if (keys(%info)) {
                           if (ref($info{$filename}) eq 'HASH') {
                               foreach my $timestamp (sort(keys(%{$info{$filename}}))) {
                                   if ($info{$filename}{$timestamp} eq $env{'user.name'}.':'.$env{'user.domain'}) {
                                       $is_valid = 1;
                                       delete($info{$filename}{$timestamp}); 
                                   }
                               }
                               $numleft = scalar(keys(%{$info{$filename}}));
                           }
                       }
                       if ($is_valid) {
                           my $result = &Apache::lonnet::removeuploadedurl($uploadurl);
                           if ($result eq 'ok') {
                               $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion successful')).'<br />');
                               if ($numleft) {
                                   &Apache::lonnet::put('scantronupload',\%info,$env{'form.domainid'},$env{'form.courseid'});
                               } else {
                                   &Apache::lonnet::del('scantronupload',[$filename],$env{'form.domainid'},$env{'form.courseid'});
                               }
                           } else {
                               $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                                         &mt('Result was [_1]',$result));
                           }
                       } else {
                           $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                                     &mt('File requested for deletion was uploaded by a different user.'));
                       }
                   } else {
                       $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                                 &mt('Filename of bubblesheet data file requested for deletion is invalid.'));
                   }
               }
           } else {
               $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'. 
                         &mt('You are not permitted to delete bubblesheet data files from the requested course.'));
           }
       } else {
           $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                             &mt('Filename of bubblesheet data file requested for deletion is invalid.'));
       }
       return;
   }
   
 sub valid_file {  sub valid_file {
     my ($requested_file)=@_;      my ($requested_file)=@_;
     foreach my $filename (sort(&scantron_filenames())) {      foreach my $filename (sort(&scantron_filenames())) {
Line 9520  sub scantron_download_scantron_data { Line 11287  sub scantron_download_scantron_data {
 ');  ');
  return;   return;
     }      }
       my (%uploader,$is_owner,%counts,$percent);
       my %uploader = &Apache::lonnet::get('scantronupload',[$file],$cdom,$cname);
       if (ref($uploader{$file}) eq 'HASH') {
           foreach my $timestamp (sort { $a <=> $b } keys(%{$uploader{$file}})) {
               if ($uploader{$file}{$timestamp} eq $env{'user.name'}.':'.$env{'user.domain'}) {
                   $is_owner = 1;
                   last;
               }
           }
       }
       unless ($is_owner) {
           &validate_uploaded_scantron_file($cdom,$cname,$symb,'scantron_orig_'.$file,'download',\%counts);
           if ($counts{'totalanysec'}) {
               my $percent_othersec = (100*$counts{'othersec'})/($counts{'totalanysec'});
               if ($percent_othersec >= 10) {
                   my $showpct = sprintf("%.0f",$percent_othersec).'%';
                   $r->print('<p class="LC_warning">'.
                             &mt('The original uploaded file includes [_1] or more of records for students for which none of your roles have rights to modify grades, so files are unavailable for download.',$showpct).
                             '</p>');
                   return;
               }
           }
       }
     my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;      my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;
     my $corrected='/uploaded/'.$cdom.'/'.$cname.'/scantron_corrected_'.$file;      my $corrected='/uploaded/'.$cdom.'/'.$cname.'/scantron_corrected_'.$file;
     my $skipped='/uploaded/'.$cdom.'/'.$cname.'/scantron_skipped_'.$file;      my $skipped='/uploaded/'.$cdom.'/'.$cname.'/scantron_skipped_'.$file;
Line 9556  sub checkscantron_results { Line 11346  sub checkscantron_results {
     my %scantron_config =      my %scantron_config =
         &Apache::lonnet::get_scantron_config($env{'form.scantron_format'});          &Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);      my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
     my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&Apache::grades::username_to_idmap($classlist);      my %idmap=&Apache::grades::username_to_idmap($classlist);
     my $navmap=Apache::lonnavmaps::navmap->new();      my $navmap=Apache::lonnavmaps::navmap->new();
Line 9567  sub checkscantron_results { Line 11357  sub checkscantron_results {
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,      my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
         %grader_randomlists_by_symb,%orderedforcode);          %grader_randomlists_by_symb,%orderedforcode);
     if (ref($map)) {      if (ref($map)) { 
         $randomorder=$map->randomorder();          $randomorder=$map->randomorder();
         $randompick=$map->randompick();          $randompick=$map->randompick();
           unless ($randomorder || $randompick) {
               foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
                   if ($res->randomorder()) {
                       $randomorder = 1;
                   }
                   if ($res->randompick()) {
                       $randompick = 1;
                   }
                   last if ($randomorder || $randompick);
               }
           }
     }      }
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
     my $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);      my $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
Line 9596  sub checkscantron_results { Line 11397  sub checkscantron_results {
         return '';          return '';
     }      }
   
     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,      &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,'Processing first student');
                                           'Processing first student');  
     my $start=&Time::HiRes::time();      my $start=&Time::HiRes::time();
     my $i=-1;      my $i=-1;
   
Line 9607  sub checkscantron_results { Line 11407  sub checkscantron_results {
         my $line=&Apache::grades::scantron_get_line($scanlines,$scan_data,$i);          my $line=&Apache::grades::scantron_get_line($scanlines,$scan_data,$i);
         if ($line=~/^[\s\cz]*$/) { next; }          if ($line=~/^[\s\cz]*$/) { next; }
         if ($started) {          if ($started) {
             &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,              &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,'last student');
                                                      'last student');  
         }          }
         $started=1;          $started=1;
         my $scan_record=          my $scan_record=
Line 9720  sub checkscantron_results { Line 11519  sub checkscantron_results {
             }              }
         }          }
     }      }
     $r->print('<p>'.      $r->print(
               &mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for [_1][quant,_2,student][_3] ([quant,_4,bubblesheet line] per student).',          '<p>'
                   '<b>',         .&mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for [_1][quant,_2,student][_3] ([quant,_4,bubblesheet line] per student).',
                   $numstudents,              '<b>',
                   '</b>',              $numstudents,
                   $env{'form.scantron_maxbubble'}).              '</b>',
               '</p>'              $env{'form.scantron_maxbubble'})
          .'</p>'
     );      );
     $r->print('<p>'      $r->print('<p>'
              .&mt('Exact matches for [_1][quant,_2,student][_3].','<b>',$passed,'</b>')               .&mt('Exact matches for [_1][quant,_2,student][_3].','<b>',$passed,'</b>')
              .'<br />'               .'<br />'
              .&mt('Discrepancies detected for [_1][quant,_2,student][_3].','<b>',$failed,'</b>')               .&mt('Discrepancies detected for [_1][quant,_2,student][_3].','<b>',$failed,'</b>')
              .'</p>');               .'</p>'
       );
     if ($passed) {      if ($passed) {
         $r->print(&mt('Students with exact correspondence between bubblesheet data and submissions are as follows:').'<br /><br />');          $r->print(&mt('Students with exact correspondence between bubblesheet data and submissions are as follows:').'<br /><br />');
         $r->print(&Apache::loncommon::start_data_table()."\n".          $r->print(&Apache::loncommon::start_data_table()."\n".
Line 9888  sub verify_scantron_grading { Line 11689  sub verify_scantron_grading {
   
 sub href_symb_cmd {  sub href_symb_cmd {
     my ($symb,$cmd)=@_;      my ($symb,$cmd)=@_;
     return '/adm/grades?symb='.&HTML::Entities::encode(&Apache::lonenc::check_encrypt($symb),'<>&"').'&amp;command='.$cmd;      return '/adm/grades?symb='.&HTML::Entities::encode(&Apache::lonenc::check_encrypt($symb),'<>&"').'&amp;command='.
              &HTML::Entities::encode($cmd,'<>&"');
 }  }
   
 sub grading_menu {  sub grading_menu {
Line 9897  sub grading_menu { Line 11699  sub grading_menu {
   
     my %fields = ('symb'=>&Apache::lonenc::check_encrypt($symb),      my %fields = ('symb'=>&Apache::lonenc::check_encrypt($symb),
                   'command'=>'individual');                    'command'=>'individual');
       
     my $url1a = &Apache::lonhtmlcommon::build_url('grades/',\%fields);      my $url1a = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
   
     $fields{'command'}='ungraded';      $fields{'command'}='ungraded';
Line 9911  sub grading_menu { Line 11713  sub grading_menu {
   
     $fields{'command'}='downloadfilesselect';      $fields{'command'}='downloadfilesselect';
     my $url1e=&Apache::lonhtmlcommon::build_url('grades/',\%fields);      my $url1e=&Apache::lonhtmlcommon::build_url('grades/',\%fields);
       
     $fields{'command'} = 'csvform';      $fields{'command'} = 'csvform';
     my $url2 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);      my $url2 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
           
Line 9936  sub grading_menu { Line 11738  sub grading_menu {
   
     my @menu = ({ categorytitle=>'Hand Grading',      my @menu = ({ categorytitle=>'Hand Grading',
             items =>[              items =>[
                         {       linktext => 'Select individual students to grade',                          { linktext => 'Select individual students to grade',
                                 url => $url1a,                      url => $url1a,
                                 permission => $permissions{'either'},                      permission => $permissions{'either'},
                                 icon => 'grade_students.png',                      icon => 'grade_students.png',
                                 linktitle => 'Grade current resource for a selection of students.'                      linktitle => 'Grade current resource for a selection of students.'
                         },                          }, 
                         {       linktext => 'Grade ungraded submissions',                          {       linktext => 'Grade ungraded submissions',
                                 url => $url1b,                                  url => $url1b,
                                 permission => $permissions{'either'},                                  permission => $permissions{'either'},
Line 9961  sub grading_menu { Line 11763  sub grading_menu {
                                 icon => 'grade_PageFolder.png',                                  icon => 'grade_PageFolder.png',
                                 linktitle => 'Grade all resources in current page/sequence/folder for one student.'                                  linktitle => 'Grade all resources in current page/sequence/folder for one student.'
                         },                          },
                         {       linktext => 'Download submitted files',                          {       linktext => 'Download submissions',
                                 url => $url1e,                                  url => $url1e,
                                 permission => $permissions{'either'},                                  permission => $permissions{'either'},
                                 icon => 'download_sub.png',                                  icon => 'download_sub.png',
                                 linktitle => 'Download all files submitted by students.'                                  linktitle => 'Download all students submissions.'
                         }]},                          }]},
                          { categorytitle=>'Automated Grading',                           { categorytitle=>'Automated Grading',
                items =>[                 items =>[
Line 9997  sub grading_menu { Line 11799  sub grading_menu {
   
                     ]                      ]
             });              });
       my $cdom = $env{"course.$env{'request.course.id'}.domain"};
       my $cnum = $env{"course.$env{'request.course.id'}.num"};
       my %passback = &Apache::lonnet::dump('nohist_linkprot_passback',$cdom,$cnum);
       if (keys(%passback)) {
           $fields{'command'} = 'initialpassback';
           my $url6 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
           push (@{$menu[1]{items}},
                     { linktext => 'Passback of Scores',
                       url => $url6,
                       permission => $permissions{'either'},
                       icon => 'passback.png',
                       linktitle => 'Passback scores to launcher CMS for resources accessed via LTI-mediated deep-linking',
                     });
       }
     # Create the menu      # Create the menu
     my $Str;      my $Str;
     $Str .= '<form method="post" action="" name="gradingMenu">';      $Str .= '<form method="post" action="" name="gradingMenu">';
Line 10035  sub submit_options_table { Line 11850  sub submit_options_table {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
     &commonJSfunctions($request);      &commonJSfunctions($request);
       my $is_tool = ($symb =~ /ext\.tool$/);
     my $result;      my $result;
   
     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".      $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
         '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";          '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
   
     $result.=&selectfield(1).      $result.=&selectfield(1,$is_tool).
             '<input type="hidden" name="command" value="viewgrades" />              '<input type="hidden" name="command" value="viewgrades" />
             <div>              <div>
               <input type="submit" value="'.&mt('Next').' &rarr;" />                <input type="submit" value="'.&mt('Next').' &rarr;" />
Line 10072  sub submit_options_download { Line 11888  sub submit_options_download {
         }          }
     }      }
   
       my $is_tool = ($symb =~ /ext\.tool$/);
     &commonJSfunctions($request);      &commonJSfunctions($request);
   
     my $result='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".      my $result='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
         $table."\n".                 $table."\n".
         '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";                 '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
     $result.='      $result.='
 <h2>  <h2>
   '.&mt('Select Students for whom to Download Submitted Files').'    '.&mt('Select Students for whom to Download Submissions').'
 </h2>'.&selectfield(1).'  </h2>'.&selectfield(1,$is_tool).'
                 <input type="hidden" name="command" value="downloadfileslink" />                  <input type="hidden" name="command" value="downloadfileslink" /> 
               <input type="submit" value="'.&mt('Next').' &rarr;" />                <input type="submit" value="'.&mt('Next').' &rarr;" />
             </div>              </div>
           </div>            </div>
Line 10096  sub submit_options { Line 11913  sub submit_options {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
       my $is_tool = ($symb =~ /ext\.tool$/);
     &commonJSfunctions($request);      &commonJSfunctions($request);
     my $result;      my $result;
   
     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".      $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
  '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";   '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
     $result.=&selectfield(1).'      $result.=&selectfield(1,$is_tool).'
                 <input type="hidden" name="command" value="submission" />                  <input type="hidden" name="command" value="submission" /> 
               <input type="submit" value="'.&mt('Next').' &rarr;" />        <input type="submit" value="'.&mt('Next').' &rarr;" />
             </div>              </div>
           </div>            </div>
   </form>';    </form>';
Line 10111  sub submit_options { Line 11929  sub submit_options {
 }  }
   
 sub selectfield {  sub selectfield {
    my ($full)=@_;     my ($full,$is_tool)=@_;
    my %options =     my %options;
        (&substatus_options,     if ($is_tool) {
         'select_form_order' => ['yes','queued','graded','incorrect','all']);         %options =
              (&transtatus_options,
               'select_form_order' => ['yes','incorrect','all']);
      } else {
          %options = 
              (&substatus_options,
               'select_form_order' => ['yes','queued','graded','incorrect','all']);
      }
   
   #    #
   # PrepareClasslist() needs to be called to avoid getting a sections list    # PrepareClasslist() needs to be called to avoid getting a sections list
   # for a different course from the @Sections global in lonstatistics.pm,    # for a different course from the @Sections global in lonstatistics.pm, 
   # populated by an earlier request.    # populated by an earlier request.
   #    #
    &Apache::lonstatistics::PrepareClasslist();     &Apache::lonstatistics::PrepareClasslist();
   
    my $result='<div class="LC_columnSection">     my $result='<div class="LC_columnSection">
     
     <fieldset>      <fieldset>
       <legend>        <legend>
        '.&mt('Sections').'         '.&mt('Sections').'
       </legend>        </legend>
       '.&Apache::lonstatistics::SectionSelect('section','multiple',5).'        '.&Apache::lonstatistics::SectionSelect('section','multiple',5).'
     </fieldset>      </fieldset>
     
     <fieldset>      <fieldset>
       <legend>        <legend>
         '.&mt('Groups').'          '.&mt('Groups').'
       </legend>        </legend>
       '.&Apache::lonstatistics::GroupSelect('group','multiple',5).'        '.&Apache::lonstatistics::GroupSelect('group','multiple',5).'
     </fieldset>      </fieldset>
      
     <fieldset>      <fieldset>
       <legend>        <legend>
         '.&mt('Access Status').'          '.&mt('Access Status').'
Line 10146  sub selectfield { Line 11971  sub selectfield {
       '.&Apache::lonhtmlcommon::StatusOptions(undef,undef,5,undef,'mult').'        '.&Apache::lonhtmlcommon::StatusOptions(undef,undef,5,undef,'mult').'
     </fieldset>';      </fieldset>';
     if ($full) {      if ($full) {
           my $heading = &mt('Submission Status');
           if ($is_tool) {
               $heading = &mt('Transaction Status');
           }
         $result.='          $result.='
     <fieldset>      <fieldset>
       <legend>        <legend>
         '.&mt('Submission Status').'          '.$heading.'
       </legend>'.        </legend>'.
        &Apache::loncommon::select_form('all','submitonly',\%options).         &Apache::loncommon::select_form('all','submitonly',\%options).
    '</fieldset>';     '</fieldset>';
Line 10182  sub reset_perm { Line 12011  sub reset_perm {
   
 sub init_perm {  sub init_perm {
     &reset_perm();      &reset_perm();
     foreach my $test_perm ('vgr','mgr','opa') {      foreach my $test_perm ('vgr','mgr','opa','usc') {
   
  my $scope = $env{'request.course.id'};   my $scope = $env{'request.course.id'};
  if (!($perm{$test_perm}=&Apache::lonnet::allowed($test_perm,$scope))) {   if (!($perm{$test_perm}=&Apache::lonnet::allowed($test_perm,$scope))) {
Line 10309  sub process_clicker { Line 12138  sub process_clicker {
     my $pcorrect=&mt("Percentage points for correct solution");      my $pcorrect=&mt("Percentage points for correct solution");
     my $pincorrect=&mt("Percentage points for incorrect solution");      my $pincorrect=&mt("Percentage points for incorrect solution");
     my $selectform=&Apache::loncommon::select_form($env{'form.upfiletype'},'upfiletype',      my $selectform=&Apache::loncommon::select_form($env{'form.upfiletype'},'upfiletype',
                                                    {'iclicker' => 'i>clicker',     {'iclicker' => 'i>clicker',
                                                     'interwrite' => 'interwrite PRS',                                                      'interwrite' => 'interwrite PRS',
                                                     'turning' => 'Turning Technologies'});                                                      'turning' => 'Turning Technologies'});
     $symb = &Apache::lonenc::check_encrypt($symb);      $symb = &Apache::lonenc::check_encrypt($symb);
Line 10712  sub turning_eval { Line 12541  sub turning_eval {
     return ($errormsg,$number);      return ($errormsg,$number);
 }  }
   
   
 sub assign_clicker_grades {  sub assign_clicker_grades {
     my ($r,$symb) = @_;      my ($r,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
Line 10724  sub assign_clicker_grades { Line 12554  sub assign_clicker_grades {
 # FIXME: This should probably look for the first handgradeable part  # FIXME: This should probably look for the first handgradeable part
     my $part=$$partlist[0];      my $part=$$partlist[0];
 # Start screen output  # Start screen output
     my $result = &Apache::loncommon::start_data_table().       my $result = &Apache::loncommon::start_data_table().
                  &Apache::loncommon::start_data_table_header_row().                   &Apache::loncommon::start_data_table_header_row().
                  '<th>'.&mt('Assigning grades based on clicker file').'</th>'.                   '<th>'.&mt('Assigning grades based on clicker file').'</th>'.
                  &Apache::loncommon::end_data_table_header_row().                   &Apache::loncommon::end_data_table_header_row().
Line 10783  sub assign_clicker_grades { Line 12613  sub assign_clicker_grades {
                       &mt('More than one entry found for [_1]!','<tt>'.$user.'</tt>').                        &mt('More than one entry found for [_1]!','<tt>'.$user.'</tt>').
                       '</span><br />';                        '</span><br />';
           }            }
           $users{$user}=1;            $users{$user}=1; 
           my @answer=split(/\,/,$env{$key});            my @answer=split(/\,/,$env{$key});
           my $sum=0;            my $sum=0;
           my $realnumber=$number;            my $realnumber=$number;
Line 10830  sub assign_clicker_grades { Line 12660  sub assign_clicker_grades {
              $result.="<br /><span class=\"LC_error\">Failed to save student $username:$domain. Message when trying to save was ($returncode)</span>";               $result.="<br /><span class=\"LC_error\">Failed to save student $username:$domain. Message when trying to save was ($returncode)</span>";
           } else {            } else {
              $storecount++;               $storecount++;
                #FIXME Do passback for $user if required
           }            }
        }         }
     }      }
Line 10858  sub startpage { Line 12689  sub startpage {
          $args{'add_entries'} = \%loaditems;           $args{'add_entries'} = \%loaditems;
     }      }
     if ($nomenu) {      if ($nomenu) {
         $args{'only_body'} = 1;          $args{'only_body'} = 1; 
         $r->print(&Apache::loncommon::start_page("Student's Version",$head_extra,\%args));          $r->print(&Apache::loncommon::start_page("Student's Version",$head_extra,\%args));
     } else {      } else {
         unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});          if ($env{'request.course.id'}) { 
               unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});
           }
         $args{'bread_crumbs'} = $crumbs;          $args{'bread_crumbs'} = $crumbs;
         $r->print(&Apache::loncommon::start_page('Grading',$head_extra,\%args));          $r->print(&Apache::loncommon::start_page('Grading',$head_extra,\%args));
           if ($env{'request.course.id'}) {
               &Apache::lonquickgrades::startGradeScreen($r,($env{'form.symb'}?'probgrading':'grading'));
           }
     }      }
     unless ($nodisplayflag) {      unless ($nodisplayflag) {
        $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp,$divforres));          $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp,$divforres));
     }      }
 }  }
   
 sub select_problem {  sub select_problem {
     my ($r)=@_;      my ($r)=@_;
     $r->print('<h3>'.&mt('Select the problem or one of the problems you want to grade').'</h3><form action="/adm/grades">');      $r->print('<h3>'.&mt('Select the problem or one of the problems you want to grade').'</h3><form action="/adm/grades">');
     $r->print(&Apache::lonstathelpers::problem_selector('.',undef,1,undef,undef,1));      $r->print(&Apache::lonstathelpers::problem_selector('.',undef,1,undef,undef,1,1));
     $r->print('<input type="hidden" name="command" value="gradingmenu" />');      $r->print('<input type="hidden" name="command" value="gradingmenu" />');
     $r->print('<input type="submit" value="'.&mt('Next').' &rarr;" /></form>');      $r->print('<input type="submit" value="'.&mt('Next').' &rarr;" /></form>');
 }  }
   
   #----- display problem, answer, and submissions for a single student (no grading)
   
   sub view_as_user {
       my ($symb,$vuname,$vudom,$hasperm) = @_;
       my $plainname = &Apache::loncommon::plainname($vuname,$vudom,'lastname');
       my $displayname = &nameUserString('',$plainname,$vuname,$vudom);
       my $output = &Apache::loncommon::get_student_view($symb,$vuname,$vudom,
                                                         $env{'request.course.id'},
                                                         undef,{'disable_submit' => 1}).
                    "\n\n".
                    '<div class="LC_grade_show_user">'.
                    '<h2>'.$displayname.'</h2>'.
                    "\n".
                    &Apache::loncommon::track_student_link('View recent activity',
                                                           $vuname,$vudom,'check').' '.
                    "\n";
       if (&Apache::lonnet::allowed('opa',$env{'request.course.id'}) ||
           (($env{'request.course.sec'} ne '') &&
            &Apache::lonnet::allowed('opa',$env{'request.course.id'}.'/'.$env{'request.course.sec'}))) {
           $output .= &Apache::loncommon::pprmlink(&mt('Set/Change parameters'),
                                                  $vuname,$vudom,$symb,'check');
       }
       $output .= "\n";
       my $companswer = &Apache::loncommon::get_student_answers($symb,$vuname,$vudom,
                                                                $env{'request.course.id'});
       $companswer=~s|<form(.*?)>||g;
       $companswer=~s|</form>||g;
       $companswer=~s|name="submit"|name="would_have_been_submit"|g;
       $output .= '<div class="LC_Box">'.
                  '<h3 class="LC_hcell">'.&mt('Correct answer for[_1]',$displayname).'</h3>'.
                  $companswer.
                  '</div>'."\n";
       my $is_tool = ($symb =~ /ext\.tool$/);
       my ($essayurl,%coursedesc_by_cid);
       (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);
       my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$vudom,$vuname);
       my $res_error;
       my ($partlist,$handgrade,$responseType,$numresp,$numessay) =
           &response_type($symb,\$res_error);
       my $fullname;
       my $collabinfo;
       if ($numessay) {
           unless ($hasperm) {
               &init_perm();
           }
           ($collabinfo,$fullname)=
               &check_collaborators($symb,$vuname,$vudom,\%record,$handgrade,0);
           unless ($hasperm) {
               &reset_perm();
           }
       }
       my $checkIcon = '<img alt="'.&mt('Check Mark').
                       '" src="'.$Apache::lonnet::perlvar{'lonIconsURL'}.
                       '/check.gif" height="16" border="0" />';
       my ($lastsubonly,$partinfo) =
           &show_last_submission($vuname,$vudom,$symb,$essayurl,$responseType,'datesub',
                                 '',$fullname,\%record,\%coursedesc_by_cid);
       $output .= '<div class="LC_Box">'.
                  '<h3 class="LC_hcell">'.&mt('Submissions').'</h3>'."\n".$collabinfo."\n";
       if (($numresp > $numessay) & !$is_tool) {
           $output .='<p class="LC_info">'.
                     &mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon).
                     "</p>\n";
       }
       $output .= $partinfo;
       $output .= $lastsubonly;
       $output .= &displaySubByDates($symb,\%record,$partlist,$responseType,$checkIcon,$vuname,$vudom);
       $output .= '</div></div>'."\n";
       return $output;
   }
   
 sub handler {  sub handler {
     my $request=$_[0];      my $request=$_[0];
     &reset_caches();      &reset_caches();
Line 10889  sub handler { Line 12796  sub handler {
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
   
 # see what command we need to execute  # see what command we need to execute
    
     my @commands=&Apache::loncommon::get_env_multiple('form.command');      my @commands=&Apache::loncommon::get_env_multiple('form.command');
     my $command=$commands[0];      my $command=$commands[0];
   
Line 10912  sub handler { Line 12819  sub handler {
  &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));   &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));
     }      }
   
   # -------------------------------------- Flag and buffer for registered cleanup
       $registered_cleanup=0;
       undef(@Apache::grades::ltipassback);
   
 # see what the symb is  # see what the symb is
   
     my $symb=$env{'form.symb'};      my $symb=$env{'form.symb'};
Line 10925  sub handler { Line 12836  sub handler {
     if (($symb eq '' || $command eq '') && ($env{'request.course.id'})) {      if (($symb eq '' || $command eq '') && ($env{'request.course.id'})) {
 #  #
 # Not called from a resource, but inside a course  # Not called from a resource, but inside a course
 #  #    
         &startpage($request,undef,[],1,1);          &startpage($request,undef,[],1,1);
         &select_problem($request);          &select_problem($request);
     } else {      } else {
         if ($command eq 'submission' && $perm{'vgr'}) {   if ($command eq 'submission' && $perm{'vgr'}) {
             my ($stuvcurrent,$stuvdisp,$versionform,$js,$onload);              my ($stuvcurrent,$stuvdisp,$versionform,$js,$onload);
             if (($env{'form.student'} ne '') && ($env{'form.userdom'} ne '')) {              if (($env{'form.student'} ne '') && ($env{'form.userdom'} ne '')) {
                 ($stuvcurrent,$stuvdisp,$versionform,$js) =                  ($stuvcurrent,$stuvdisp,$versionform,$js) =
Line 10958  sub handler { Line 12869  sub handler {
                 }                  }
                 $request->print($versionform);                  $request->print($versionform);
             }              }
             ($env{'form.student'} eq '' ? &listStudents($request,$symb,'',$divforres) : &submission($request,0,0,$symb,$divforres,$command));      ($env{'form.student'} eq '' ? &listStudents($request,$symb,'',$divforres) : &submission($request,0,0,$symb,$divforres,$command));
         } elsif ($command eq 'versionsub' && $perm{'vgr'}) {          } elsif ($command eq 'versionsub' && $perm{'vgr'}) {
             my ($stuvcurrent,$stuvdisp,$versionform,$js) =              my ($stuvcurrent,$stuvdisp,$versionform,$js) =
                 &choose_task_version_form($symb,$env{'form.student'},                  &choose_task_version_form($symb,$env{'form.student'},
Line 10978  sub handler { Line 12889  sub handler {
             }              }
             $request->print('<br clear="all" />');              $request->print('<br clear="all" />');
             $request->print(&show_previous_task_version($request,$symb));              $request->print(&show_previous_task_version($request,$symb));
         } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {   } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
                                        {href=>'',text=>'Select student'}],1,1);                                         {href=>'',text=>'Select student'}],1,1);
             &pickStudentPage($request,$symb);      &pickStudentPage($request,$symb);
         } elsif ($command eq 'displayPage' && $perm{'vgr'}) {   } elsif ($command eq 'displayPage' && $perm{'vgr'}) {
             my $csslinks;              my $csslinks;
             unless ($env{'form.vProb'} eq 'no') {              unless ($env{'form.vProb'} eq 'no') {
                 $csslinks = &Apache::loncommon::css_links($symb,'map');                  $csslinks = &Apache::loncommon::css_links($symb,'map');
Line 10991  sub handler { Line 12902  sub handler {
                                       [{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},                                        [{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
                                        {href=>'',text=>'Select student'},                                         {href=>'',text=>'Select student'},
                                        {href=>'',text=>'Grade student'}],1,1,undef,undef,undef,$csslinks);                                         {href=>'',text=>'Grade student'}],1,1,undef,undef,undef,$csslinks);
             &displayPage($request,$symb);      &displayPage($request,$symb);
         } elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {   } elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
                                        {href=>'',text=>'Select student'},                                         {href=>'',text=>'Select student'},
                                        {href=>'',text=>'Grade student'},                                         {href=>'',text=>'Grade student'},
                                        {href=>'',text=>'Store grades'}],1,1);                                         {href=>'',text=>'Store grades'}],1,1);
             &updateGradeByPage($request,$symb);      &updateGradeByPage($request,$symb);
         } elsif ($command eq 'processGroup' && $perm{'vgr'}) {   } elsif ($command eq 'processGroup' && $perm{'vgr'}) {
             my $csslinks;              my $csslinks;
             unless ($env{'form.vProb'} eq 'no') {              unless ($env{'form.vProb'} eq 'no') {
                 $csslinks = &Apache::loncommon::css_links($symb);                  $csslinks = &Apache::loncommon::css_links($symb);
             }              }
             &startpage($request,$symb,[{href=>'',text=>'...'},              &startpage($request,$symb,[{href=>'',text=>'...'},
                                        {href=>'',text=>'Modify grades'}],undef,undef,undef,undef,undef,$csslinks,undef,1);                                         {href=>'',text=>'Modify grades'}],undef,undef,undef,undef,undef,$csslinks,undef,1);
             &processGroup($request,$symb);      &processGroup($request,$symb);
         } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {   } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {
             &startpage($request,$symb);              &startpage($request,$symb);
             $request->print(&grading_menu($request,$symb));      $request->print(&grading_menu($request,$symb));
         } elsif ($command eq 'individual' && $perm{'vgr'}) {   } elsif ($command eq 'individual' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>'',text=>'Select individual students to grade'}]);              &startpage($request,$symb,[{href=>'',text=>'Select individual students to grade'}]);
             $request->print(&submit_options($request,$symb));      $request->print(&submit_options($request,$symb));
         } elsif ($command eq 'ungraded' && $perm{'vgr'}) {          } elsif ($command eq 'ungraded' && $perm{'vgr'}) {
             my $js = &part_selector_js();              my $js = &part_selector_js();
             my $onload = "toggleParts('gradesub');";              my $onload = "toggleParts('gradesub');";
Line 11024  sub handler { Line 12935  sub handler {
         } elsif ($command eq 'all_for_one' && $perm{'vgr'}) {          } elsif ($command eq 'all_for_one' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>'',text=>'Grade page/folder for one student'}],1,1);              &startpage($request,$symb,[{href=>'',text=>'Grade page/folder for one student'}],1,1);
             $request->print(&submit_options_sequence($request,$symb));              $request->print(&submit_options_sequence($request,$symb));
         } elsif ($command eq 'viewgrades' && $perm{'vgr'}) {   } elsif ($command eq 'viewgrades' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"table"), text=>"Grading table"},{href=>'', text=>"Modify grades"}]);              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"table"), text=>"Grading table"},{href=>'', text=>"Modify grades"}]);
             $request->print(&viewgrades($request,$symb));      $request->print(&viewgrades($request,$symb));
         } elsif ($command eq 'handgrade' && $perm{'mgr'}) {   } elsif ($command eq 'handgrade' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'',text=>'...'},              &startpage($request,$symb,[{href=>'',text=>'...'},
                                        {href=>'',text=>'Store grades'}]);                                         {href=>'',text=>'Store grades'}]);
             $request->print(&processHandGrade($request,$symb));      $request->print(&processHandGrade($request,$symb));
         } elsif ($command eq 'editgrades' && $perm{'mgr'}) {   } elsif ($command eq 'editgrades' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"table"), text=>"Grading table"},              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"table"), text=>"Grading table"},
                                        {href=>&href_symb_cmd($symb,'viewgrades').'&group=all&section=all&Status=Active',                                         {href=>&href_symb_cmd($symb,'viewgrades').'&group=all&section=all&Status=Active',
                                                                              text=>"Modify grades"},                                                                               text=>"Modify grades"},
                                        {href=>'', text=>"Store grades"}]);                                         {href=>'', text=>"Store grades"}]);
             $request->print(&editgrades($request,$symb));      $request->print(&editgrades($request,$symb));
         } elsif ($command eq 'initialverifyreceipt' && $perm{'vgr'}) {          } elsif ($command eq 'initialverifyreceipt' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>'',text=>'Verify Receipt Number'}]);              &startpage($request,$symb,[{href=>'',text=>'Verify Receipt Number'}]);
             $request->print(&initialverifyreceipt($request,$symb));              $request->print(&initialverifyreceipt($request,$symb));
         } elsif ($command eq 'verify' && $perm{'vgr'}) {   } elsif ($command eq 'verify' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"initialverifyreceipt"),text=>'Verify Receipt Number'},              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"initialverifyreceipt"),text=>'Verify Receipt Number'},
                                        {href=>'',text=>'Verification Result'}]);                                         {href=>'',text=>'Verification Result'}]);
             $request->print(&verifyreceipt($request,$symb));      $request->print(&verifyreceipt($request,$symb));
         } elsif ($command eq 'processclicker' && $perm{'mgr'}) {          } elsif ($command eq 'processclicker' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Process clicker'}]);              &startpage($request,$symb,[{href=>'', text=>'Process clicker'}]);
             $request->print(&process_clicker($request,$symb));              $request->print(&process_clicker($request,$symb));
Line 11056  sub handler { Line 12967  sub handler {
                                        {href=>'', text=>'Process clicker file'},                                         {href=>'', text=>'Process clicker file'},
                                        {href=>'', text=>'Store grades'}]);                                         {href=>'', text=>'Store grades'}]);
             $request->print(&assign_clicker_grades($request,$symb));              $request->print(&assign_clicker_grades($request,$symb));
         } elsif ($command eq 'csvform' && $perm{'mgr'}) {   } elsif ($command eq 'csvform' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
             $request->print(&upcsvScores_form($request,$symb));      $request->print(&upcsvScores_form($request,$symb));
         } elsif ($command eq 'csvupload' && $perm{'mgr'}) {   } elsif ($command eq 'csvupload' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
             $request->print(&csvupload($request,$symb));      $request->print(&csvupload($request,$symb));
         } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {   } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {
             &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
             $request->print(&csvuploadmap($request,$symb));      $request->print(&csvuploadmap($request,$symb));
         } elsif ($command eq 'csvuploadoptions' && $perm{'mgr'}) {   } elsif ($command eq 'csvuploadoptions' && $perm{'mgr'}) {
             if ($env{'form.associate'} ne 'Reverse Association') {      if ($env{'form.associate'} ne 'Reverse Association') {
                 &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);                  &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
                 $request->print(&csvuploadoptions($request,$symb));   $request->print(&csvuploadoptions($request,$symb));
             } else {      } else {
                 if ( $env{'form.upfile_associate'} ne 'reverse' ) {   if ( $env{'form.upfile_associate'} ne 'reverse' ) {
                     $env{'form.upfile_associate'} = 'reverse';      $env{'form.upfile_associate'} = 'reverse';
                 } else {   } else {
                     $env{'form.upfile_associate'} = 'forward';      $env{'form.upfile_associate'} = 'forward';
                 }   }
                 &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);                  &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
                 $request->print(&csvuploadmap($request,$symb));   $request->print(&csvuploadmap($request,$symb));
             }      }
         } elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) {   } elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) {
             &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
             $request->print(&csvuploadassign($request,$symb));      $request->print(&csvuploadassign($request,$symb));
         } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {   } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,
                        undef,undef,undef,undef,'toggleScantab(document.rules);');                         undef,undef,undef,undef,'toggleScantab(document.rules);');
             $request->print(&scantron_selectphase($request,undef,$symb));      $request->print(&scantron_selectphase($request,undef,$symb));
         } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {    } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&scantron_do_warning($request,$symb));       $request->print(&scantron_do_warning($request,$symb));
         } elsif ($command eq 'scantron_validate' && $perm{'mgr'}) {   } elsif ($command eq 'scantron_validate' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&scantron_validate_file($request,$symb));      $request->print(&scantron_validate_file($request,$symb));
         } elsif ($command eq 'scantron_process' && $perm{'mgr'}) {   } elsif ($command eq 'scantron_process' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&scantron_process_students($request,$symb));      $request->print(&scantron_process_students($request,$symb));
         } elsif ($command eq 'scantronupload' &&    } elsif ($command eq 'scantronupload' && 
                  (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||    (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
                   &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {  
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,
                        undef,undef,undef,undef,'toggleScantab(document.rules);');                         undef,undef,undef,undef,'toggleScantab(document.rules);');
             $request->print(&scantron_upload_scantron_data($request,$symb));       $request->print(&scantron_upload_scantron_data($request,$symb)); 
         } elsif ($command eq 'scantronupload_save' &&    } elsif ($command eq 'scantronupload_save' &&
                  (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||    (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
                   &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {  
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&scantron_upload_scantron_data_save($request,$symb));       $request->print(&scantron_upload_scantron_data_save($request,$symb));
         } elsif ($command eq 'scantron_download' &&    } elsif ($command eq 'scantron_download' && ($perm{'usc'} || $perm{'mgr'})) {
                  &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {  
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&scantron_download_scantron_data($request,$symb));       $request->print(&scantron_download_scantron_data($request,$symb));
           } elsif ($command eq 'scantronupload_delete' &&
                    (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
               &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
               &scantron_upload_delete($request,$symb);
         } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {          } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&checkscantron_results($request,$symb));              $request->print(&checkscantron_results($request,$symb));
Line 11125  sub handler { Line 13037  sub handler {
                undef,undef,undef,undef,undef,undef,undef,1);                 undef,undef,undef,undef,undef,undef,undef,1);
             $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');              $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');
             &submit_download_link($request,$symb);              &submit_download_link($request,$symb);
         } elsif ($command) {          } elsif ($command eq 'initialpassback') {
               &startpage($request,$symb,[{href=>'', text=>'Choose Launcher'}],undef,1);
               $request->print(&initialpassback($request,$symb));
           } elsif ($command eq 'passback') {
               &startpage($request,$symb,
                          [{href=>&href_symb_cmd($symb,'initialpassback'), text=>'Choose Launcher'},
                           {href=>'', text=>'Types of User'}],undef,1);
               $request->print(&passback_filters($request,$symb));
           } elsif ($command eq 'passbacknames') {
               my $chosen;
               if ($env{'form.passback'} ne '') {
                   if ($env{'form.passback'} eq &unescape($env{'form.passback'})) {
                       $env{'form.passback'} = &escape($env{'form.passback'} );
                   }
                   $chosen = &HTML::Entities::encode($env{'form.passback'},'<>"&');
               }
               &startpage($request,$symb,
                          [{href=>&href_symb_cmd($symb,'initialpassback'), text=>'Choose Launcher'},
                           {href=>&href_symb_cmd($symb,'passback').'&amp;passback='.$chosen, text=>'Types of User'},
                           {href=>'', text=>'Select Users'}],undef,1);
               $request->print(&names_for_passback($request,$symb));
           } elsif ($command eq 'passbackscores') {
               my ($chosen,$stu_status);
               if ($env{'form.passback'} ne '') {
                   if ($env{'form.passback'} eq &unescape($env{'form.passback'})) {
                       $env{'form.passback'} = &escape($env{'form.passback'} );
                   }
                   $chosen = &HTML::Entities::encode($env{'form.passback'},'<>"&');
               }
               if ($env{'form.Status'}) {
                   $stu_status = &HTML::Entities::encode($env{'form.Status'});
               }
               &startpage($request,$symb,
                          [{href=>&href_symb_cmd($symb,'initialpassback'), text=>'Choose Launcher'},
                           {href=>&href_symb_cmd($symb,'passback').'&amp;passback='.$chosen, text=>'Types of User'},
                           {href=>&href_symb_cmd($symb,'passbacknames').'&amp;Status='.$stu_status.'&amp;passback='.$chosen, text=>'Select Users'},
                           {href=>'', text=>'Execute Passback'}],undef,1);
               $request->print(&do_passback($request,$symb));
    } elsif ($command) {
             &startpage($request,$symb,[{href=>'', text=>'Access denied'}]);              &startpage($request,$symb,[{href=>'', text=>'Access denied'}]);
             $request->print('<p class="LC_error">'.&mt('Access Denied ([_1])',$command).'</p>');      $request->print('<p class="LC_error">'.&mt('Access Denied ([_1])',$command).'</p>');
         }   }
     }      }
     if ($ssi_error) {      if ($ssi_error) {
  &ssi_print_error($request);   &ssi_print_error($request);
     }      }
     $request->print(&Apache::loncommon::end_page());      if ($env{'form.inhibitmenu'}) {
           $request->print(&Apache::loncommon::end_page());
       } elsif ($env{'request.course.id'}) {
           &Apache::lonquickgrades::endGradeScreen($request);
       }
     &reset_caches();      &reset_caches();
     return OK;      return OK;
 }  }
Line 11157  described at http://www.lon-capa.org. Line 13111  described at http://www.lon-capa.org.
 =head1 OVERVIEW  =head1 OVERVIEW
   
 Do an ssi with retries:  Do an ssi with retries:
 While I'd love to factor out this with the vesrion in lonprintout,  While I'd love to factor out this with the version in lonprintout,
 that would either require a data coupling between modules, which I refuse to perpetuate (there's quite enough of that already), or would require the invention of another infrastructure  that would either require a data coupling between modules, which I refuse to perpetuate (there's quite enough of that already), or would require the invention of another infrastructure
 I'm not quite ready to invent (e.g. an ssi_with_retry object).  I'm not quite ready to invent (e.g. an ssi_with_retry object).
   
Line 11204  ssi_with_retries() Line 13158  ssi_with_retries()
   
 =over  =over
   
   =head1 Routines to display previous version of a Task for a specific student
   
   Tasks are graded pass/fail. Students who have yet to pass a particular Task
   can receive another opportunity. Access to tasks is slot-based. If a slot
   requires a proctor to check-in the student, a new version of the Task will
   be created when the student is checked in to the new opportunity.
   
   If a particular student has tried two or more versions of a particular task,
   the submission screen provides a user with vgr privileges (e.g., a Course
   Coordinator) the ability to display a previous version worked on by the
   student.  By default, the current version is displayed. If a previous version
   has been selected for display, submission data are only shown that pertain
   to that particular version, and the interface to submit grades is not shown.
   
   =over 4
   
   =item show_previous_task_version()
   
   Displays a specified version of a student's Task, as the student sees it.
   
   Inputs: 2
           request - request object
           symb    - unique symb for current instance of resource
   
   Output: None.
   
   Side Effects: calls &show_problem() to print version of Task, with
                 version contained in form item: $env{'form.previousversion'}
   
   =item choose_task_version_form()
   
   Displays a web form used to select which version of a student's view of a
   Task should be displayed.  Either launches a pop-up window, or replaces
   content in existing pop-up, or replaces page in main window.
   
   Inputs: 4
           symb    - unique symb for current instance of resource
           uname   - username of student
           udom    - domain of student
           nomenu  - 1 if display is in a pop-up window, and hence no menu
                     breadcrumbs etc., are displayed
   
   Output: 4
           current   - student's current version
           displayed - student's version being displayed
           result    - scalar containing HTML for web form used to switch to
                       a different version (or a link to close window, if pop-up).
           js        - javascript for processing selection in versions web form
   
   Side Effects: None.
   
   =item previous_display_javascript()
   
   Inputs: 2
           nomenu  - 1 if display is in a pop-up window, and hence no menu
                     breadcrumbs etc., are displayed.
           current - student's current version number.
   
   Output: 1
           js      - javascript for processing selection in versions web form.
   
   Side Effects: None.
   
   =back
   
   =head1 Routines to process bubblesheet data.
   
   =over 4
   
 =item scantron_get_correction() :   =item scantron_get_correction() : 
   
    Builds the interface screen to interact with the operator to fix a     Builds the interface screen to interact with the operator to fix a
Line 11230  ssi_with_retries() Line 13253  ssi_with_retries()
          - missingbubble - array ref of the bubble lines that have missing           - missingbubble - array ref of the bubble lines that have missing
                            bubble errors                             bubble errors
   
    $randomorder - True if exam folder has randomorder set     $randomorder - True if exam folder (or a sub-folder) has randomorder set
    $randompick  - True if exam folder has randompick set     $randompick  - True if exam folder (or a sub-folder) has randompick set
    $respnumlookup - Reference to HASH mapping question numbers in bubble lines     $respnumlookup - Reference to HASH mapping question numbers in bubble lines
                      for current line to question number used for same question                       for current line to question number used for same question
                      in "Master Seqence" (as seen by Course Coordinator).                       in "Master Seqence" (as seen by Course Coordinator).
Line 11240  ssi_with_retries() Line 13263  ssi_with_retries()
                   or code-based randompick and/or randomorder.                    or code-based randompick and/or randomorder.
   
   
   
 =item  scantron_get_maxbubble() :   =item  scantron_get_maxbubble() : 
   
    Arguments:     Arguments:
Line 11299  ssi_with_retries() Line 13323  ssi_with_retries()
 =item  scantron_upload_scantron_data_save() :   =item  scantron_upload_scantron_data_save() : 
   
    Adds a provided bubble information data file to the course if user     Adds a provided bubble information data file to the course if user
    has the correct privileges to do so.      has the correct privileges to do so.
   
   = item scantron_upload_delete() :
   
      Deletes a previously uploaded bubble information data file, if user
      was the one who uploaded the file, and has the privileges to do so.
   
 =item  valid_file() :  =item  valid_file() :
   
Line 11319  ssi_with_retries() Line 13348  ssi_with_retries()
 =item navmap_errormsg() :  =item navmap_errormsg() :
   
    Returns HTML mark-up inside a <div></div> with a link to re-initialize the course.     Returns HTML mark-up inside a <div></div> with a link to re-initialize the course.
    Should be called whenever the request to instantiate a navmap object fails.       Should be called whenever the request to instantiate a navmap object fails.
   
   =back
   
 =back  =back
   

Removed from v.1.596.2.12.2.57  
changed lines
  Added in v.1.801


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