Diff for /loncom/homework/grades.pm between versions 1.596.2.12.2.60.2.9 and 1.742

version 1.596.2.12.2.60.2.9, 2025/01/18 21:29:42 version 1.742, 2017/08/11 18:58:17
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::loncourserespicker;  
 use String::Similarity;  use String::Similarity;
 use HTML::Parser();  
 use File::MMagic;  
 use LONCAPA;  use LONCAPA;
 use LONCAPA::ltiutils();  
   
 use POSIX qw(floor);  use POSIX qw(floor);
   
Line 66  my $ssi_retries = 5; Line 62  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 120  sub getpartlist { Line 116  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 $toolsymb;      my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys'));
     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 149  sub nameUserString { Line 141  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. ---  #--- Indicate if a response type is coded handgraded or not. ---
 #--- 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 {
     my ($symb,$response_error) = @_;      my ($symb,$response_error) = @_;
Line 167  sub response_type { Line 158  sub response_type {
         return;          return;
     }      }
     my $partlist = $res->parts();      my $partlist = $res->parts();
     my ($numresp,$numessay,$numdropbox) = (0,0,0);  
     my %vPart =       my %vPart = 
  map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart'));   map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart'));
     my (%response_types,%handgrade);      my (%response_types,%handgrade);
Line 177  sub response_type { Line 167  sub response_type {
  my @types = $res->responseType($part);   my @types = $res->responseType($part);
  my @ids = $res->responseIds($part);   my @ids = $res->responseIds($part);
  for (my $i=0; $i < scalar(@ids); $i++) {   for (my $i=0; $i < scalar(@ids); $i++) {
             $numresp ++;  
     $response_types{$part}{$ids[$i]} = $types[$i];      $response_types{$part}{$ids[$i]} = $types[$i];
             if ($types[$i] eq 'essay') {  
                 $numessay ++;  
                 if (&Apache::lonnet::EXT("resource.$part".'_'.$ids[$i].".uploadedfiletypes",$symb)) {  
                     $numdropbox ++;  
                 }  
             }  
     $handgrade{$part.'_'.$ids[$i]} =       $handgrade{$part.'_'.$ids[$i]} = 
  &Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i].   &Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i].
      '.handgrade',$symb);       '.handgrade',$symb);
  }   }
     }      }
     return ($partlist,\%handgrade,\%response_types,$numresp,$numessay,$numdropbox);      return ($partlist,\%handgrade,\%response_types);
 }  }
   
 sub flatten_responseType {  sub flatten_responseType {
Line 217  sub get_display_part { Line 200  sub get_display_part {
     return $display;      return $display;
 }  }
   
 #--- Show parts and response type  
 sub showResourceInfo {  
     my ($symb,$partlist,$responseType,$formname,$checkboxes,$uploads) = @_;  
     unless ((ref($partlist) eq 'ARRAY') && (ref($responseType) eq 'HASH')) {  
         return '<br clear="all">';  
     }  
     my $coltitle = &mt('Problem Part Shown');  
     if ($checkboxes) {  
         $coltitle = &mt('Problem Part');  
     } else {  
         my $checkedparts = 0;  
         foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {  
             if (grep(/^\Q$partid\E$/,@{$partlist})) {  
                 $checkedparts ++;  
             }  
         }  
         if ($checkedparts == scalar(@{$partlist})) {  
             return '<br clear="all">';  
         }  
         if ($uploads) {  
             $coltitle = &mt('Problem Part Selected');  
         }  
     }  
     my $result = '<div class="LC_left_float" style="display:inline-block;">';  
     if ($checkboxes) {  
         my $legend = &mt('Parts to display');  
         if ($uploads) {  
             $legend = &mt('Part(s) with dropbox');  
         }  
         $result .= '<fieldset style="display:inline-block;"><legend>'.$legend.'</legend>'.  
                    '<span class="LC_nobreak">'.  
                    '<label><input type="radio" name="chooseparts" value="0" onclick="toggleParts('."'$formname'".');" checked="checked" />'.  
                    &mt('All parts').'</label>'.('&nbsp;'x2).  
                    '<label><input type="radio" name="chooseparts" value="1" onclick="toggleParts('."'$formname'".');" />'.  
                    &mt('Selected parts').'</label></span>'.  
                    '<div id="LC_partselector" style="display:none">';  
     }  
     $result .= &Apache::loncommon::start_data_table()  
               .&Apache::loncommon::start_data_table_header_row();  
     if ($checkboxes) {  
         $result .= '<th>'.&mt('Display?').'</th>';  
     }  
     $result .= '<th>'.$coltitle.'</th>'  
               .'<th>'.&mt('Res. ID').'</th>'  
               .'<th>'.&mt('Type').'</th>'  
               .&Apache::loncommon::end_data_table_header_row();  
     my %partsseen;  
     foreach my $partID (sort(keys(%$responseType))) {  
         foreach my $resID (sort(keys(%{ $responseType->{$partID} }))) {  
             my $responsetype = $responseType->{$partID}->{$resID};  
             if ($uploads) {  
                 next unless ($responsetype eq 'essay');  
                 next unless (&Apache::lonnet::EXT("resource.$partID".'_'."$resID.uploadedfiletypes",$symb));  
             }  
             my $display_part=&get_display_part($partID,$symb);  
             if (exists($partsseen{$partID})) {  
                 $result.=&Apache::loncommon::continue_data_table_row();  
             } else {  
                 $partsseen{$partID}=scalar(keys(%{$responseType->{$partID}}));  
                 $result.=&Apache::loncommon::start_data_table_row().  
                          '<td rowspan="'.$partsseen{$partID}.'" style="vertical-align:middle">';  
                 if ($checkboxes) {  
                     $result.='<input type="checkbox" name="vPart" checked="checked" value="'.$partID.'" /></td>'.  
                              '<td rowspan="'.$partsseen{$partID}.'" style="vertical-align:middle">'.$display_part.'</td>';  
                 } else {  
                     $result.=$display_part.'</td>';  
                 }  
             }  
             $result.='<td>'.'<span class="LC_internal_info">'.$resID.'</span></td>'  
                     .'<td>'.&mt($responsetype).'</td>'  
                     .&Apache::loncommon::end_data_table_row();  
         }  
     }  
     $result.=&Apache::loncommon::end_data_table();  
     if ($checkboxes) {  
         $result .= '</div></fieldset>';  
     }  
     $result .= '</div><div style="padding:0;clear:both;margin:0;border:0"></div>';  
     if (!keys(%partsseen)) {  
         $result = '';  
         if ($uploads) {  
             return '<div style="padding:0;clear:both;margin:0;border:0"></div>'.  
                    '<p class="LC_info">'.  
                     &mt('No dropbox items or essayresponse items with uploadedfiletypes set.').  
                    '</p>';  
         } else {  
             return '<br clear="all" />';  
         }  
     }  
     return $result;  
 }  
   
 sub part_selector_js {  
     my $js = <<"END";  
 function toggleParts(formname) {  
     if (document.getElementById('LC_partselector')) {  
         var index = '';  
         if (document.forms.length) {  
             for (var i=0; i<document.forms.length; i++) {  
                 if (document.forms[i].name == formname) {  
                     index = i;  
                     break;  
                 }  
             }  
         }  
         if ((index != '') && (document.forms[index].elements['chooseparts'].length > 1)) {  
             for (var i=0; i<document.forms[index].elements['chooseparts'].length; i++) {  
                 if (document.forms[index].elements['chooseparts'][i].checked) {  
                    var val = document.forms[index].elements['chooseparts'][i].value;  
                     if (document.forms[index].elements['chooseparts'][i].value == 1) {  
                         document.getElementById('LC_partselector').style.display = 'block';  
                     } else {  
                         document.getElementById('LC_partselector').style.display = 'none';  
                     }  
                 }  
             }  
         }  
     }  
 }  
 END  
     return &Apache::lonhtmlcommon::scripttag($js);  
 }  
   
 sub reset_caches {  sub reset_caches {
     &reset_analyze_cache();      &reset_analyze_cache();
     &reset_perm();      &reset_perm();
Line 448  sub reset_caches { Line 308  sub reset_caches {
                     $add_to_form = { 'code_for_randomlist' => $scancode,};                      $add_to_form = { 'code_for_randomlist' => $scancode,};
                 }                  }
             }              }
             my $analyze =              my $analyze = 
                 &get_analyze($symb,$uname,$udom,undef,$add_to_form,                  &get_analyze($symb,$uname,$udom,undef,$add_to_form,
                              undef,undef,undef,$bubbles_per_row);                               undef,undef,undef,$bubbles_per_row);
             if (ref($analyze) eq 'HASH') {              if (ref($analyze) eq 'HASH') {
Line 478  sub cleanRecord { Line 338  sub cleanRecord {
     if ($response =~ /^(option|rank)$/) {      if ($response =~ /^(option|rank)$/) {
  my %answer=&Apache::lonnet::str2hash($answer);   my %answer=&Apache::lonnet::str2hash($answer);
         my @answer = %answer;          my @answer = %answer;
         %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer;          %answer = map {&HTML::Entities::encode($_, '"<>&')}  @answer;
  my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});   my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
  my ($toprow,$bottomrow);   my ($toprow,$bottomrow);
  foreach my $foil (@$order) {   foreach my $foil (@$order) {
Line 496  sub cleanRecord { Line 356  sub cleanRecord {
     } elsif ($response eq 'match') {      } elsif ($response eq 'match') {
  my %answer=&Apache::lonnet::str2hash($answer);   my %answer=&Apache::lonnet::str2hash($answer);
         my @answer = %answer;          my @answer = %answer;
         %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer;          %answer = map {&HTML::Entities::encode($_, '"<>&')}  @answer;
  my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});   my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
  my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"});   my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"});
  my ($toprow,$middlerow,$bottomrow);   my ($toprow,$middlerow,$bottomrow);
Line 553  sub cleanRecord { Line 413  sub cleanRecord {
     $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';      $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
     $env{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.      $env{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.
  }   }
         $answer = &Apache::lontexconvert::msgtexconverted($answer);  
  return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>';   return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>';
   
     } elsif ( $response eq 'organic') {      } elsif ( $response eq 'organic') {
         my $result=&mt('Smile representation: [_1]',          my $result=&mt('Smile representation: [_1]',
                            '"<tt>'.&HTML::Entities::encode($answer, '"<>&').'</tt>"');                             '"<tt>'.&HTML::Entities::encode($answer, '"<>&').'</tt>"');
Line 638  COMMONJSFUNCTIONS Line 498  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,$filterbypbid,$possibles) = @_;      my ($getsec,$filterlist,$getgroup) = @_;
     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 666  sub getclasslist { Line 526  sub getclasslist {
     #      #
     my %sections;      my %sections;
     my %fullnames;      my %fullnames;
     my %passback;  
     my ($cdom,$cnum,$partlist);  
     if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {  
         $cdom = $env{"course.$env{'request.course.id'}.domain"};  
         $cnum = $env{"course.$env{'request.course.id'}.num"};  
         my $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      = 
             $classlist->{$student}->[&Apache::loncoursedata::CL_END()];              $classlist->{$student}->[&Apache::loncoursedata::CL_END()];
Line 693  sub getclasslist { Line 542  sub getclasslist {
         my $group   =           my $group   = 
             $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];              $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
  # filter students according to status selected   # filter students according to status selected
  if ($filterbyaccstatus && (!($stu_status =~ /Any/))) {   if ($filterlist && (!($stu_status =~ /Any/))) {
     if (!($stu_status =~ $status)) {      if (!($stu_status =~ $status)) {
  delete($classlist->{$student});   delete($classlist->{$student});
  next;   next;
Line 710  sub getclasslist { Line 559  sub getclasslist {
                }                  } 
         }          }
            if (($grp eq 'none') && !$group) {             if (($grp eq 'none') && !$group) {
            $exclude = 0;                 $exclude = 0;
         }          }
     }      }
     if ($exclude) {      if ($exclude) {
         delete($classlist->{$student});          delete($classlist->{$student});
  next;  
     }      }
  }   }
         if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {  
             my $udom =  
                 $classlist->{$student}->[&Apache::loncoursedata::CL_SDOM()];  
             my $uname =  
                 $classlist->{$student}->[&Apache::loncoursedata::CL_SNAME()];  
             if (($symb ne '') && ($udom ne '') && ($uname ne '')) {  
                 if ($submitonly eq 'queued') {  
                     my %queue_status =  
                         &Apache::bridgetask::get_student_status($symb,$cdom,$cnum,  
                                                                 $udom,$uname);  
                     if (!defined($queue_status{'gradingqueue'})) {  
                         delete($classlist->{$student});  
                         next;  
                     }  
                 } else {  
                     my (%status) =&student_gradeStatus($symb,$udom,$uname,$partlist);  
                     my $submitted = 0;  
                     my $graded = 0;  
                     my $incorrect = 0;  
                     foreach (keys(%status)) {  
                         $submitted = 1 if ($status{$_} ne 'nothing');  
                         $graded = 1 if ($status{$_} =~ /^ungraded/);  
                         $incorrect = 1 if ($status{$_} =~ /^incorrect/);  
   
                         my ($foo,$partid,$foo1) = split(/\./,$_);  
                         if ($status{'resource.'.$partid.'.submitted_by'} ne '') {  
                             $submitted = 0;  
                         }  
                     }  
                     if (!$submitted && ($submitonly eq 'yes' ||  
                                         $submitonly eq 'incorrect' ||  
                                         $submitonly eq 'graded')) {  
                         delete($classlist->{$student});  
                         next;  
                     } elsif (!$graded && ($submitonly eq 'graded')) {  
                         delete($classlist->{$student});  
                         next;  
                     } elsif (!$incorrect && $submitonly eq 'incorrect') {  
                         delete($classlist->{$student});  
                         next;  
                     }  
                 }  
             }  
         }  
         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 797  sub getclasslist { Line 580  sub getclasslist {
     delete($classlist->{$student});      delete($classlist->{$student});
  }   }
     }      }
       my %seen = ();
     my @sections = sort(keys(%sections));      my @sections = sort(keys(%sections));
     return ($classlist,\@sections,\%fullnames,\%passback);      return ($classlist,\@sections,\%fullnames);
 }  }
   
 sub canmodify {  sub canmodify {
Line 812  sub canmodify { Line 596  sub canmodify {
  #can modify the requested section   #can modify the requested section
  return 1;   return 1;
     } else {      } else {
  # can't modify the requested section   # can't modify the request section
  return 0;   return 0;
     }      }
  }   }
Line 825  sub canview { Line 609  sub canview {
     my ($sec)=@_;      my ($sec)=@_;
     if ($perm{'vgr'}) {      if ($perm{'vgr'}) {
  if (!defined($perm{'vgr_section'})) {   if (!defined($perm{'vgr_section'})) {
     # can view whole class      # can modify whole class
     return 1;      return 1;
  } else {   } else {
     if ($sec eq $perm{'vgr_section'}) {      if ($sec eq $perm{'vgr_section'}) {
  #can view the requested section   #can modify the requested section
  return 1;   return 1;
     } else {      } else {
  # can't view the requested section   # can't modify the request section
  return 0;   return 0;
     }      }
  }   }
     }      }
     #can't view      #can't modify
     return 0;      return 0;
 }  }
   
Line 978  sub initialverifyreceipt { Line 762  sub initialverifyreceipt {
   
 #--- Check whether a receipt number is valid.---  #--- Check whether a receipt number is valid.---
 sub verifyreceipt {  sub verifyreceipt {
     my ($request,$symb) = @_;      my ($request,$symb)  = @_;
   
     my $courseid = $env{'request.course.id'};      my $courseid = $env{'request.course.id'};
     my $receipt  = &Apache::lonnet::recprefix($courseid).'-'.      my $receipt  = &Apache::lonnet::recprefix($courseid).'-'.
  $env{'form.receipt'};   $env{'form.receipt'};
     $receipt     =~ s/[^\-\d]//g;      $receipt     =~ s/[^\-\d]//g;
   
     my $title =      my $title.=
  '<h3><span class="LC_info">'.   '<h3><span class="LC_info">'.
  &mt('Verifying Receipt Number [_1]',$receipt).   &mt('Verifying Receipt Number [_1]',$receipt).
  '</span></h3>'."\n";   '</span></h3>'."\n";
Line 1061  sub verifyreceipt { Line 845  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 ($launchsymb,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);  
     if ($env{'form.passback'} ne '') {  
         $chosen = &unescape($env{'form.passback'});  
         ($linkuri,$linkprotector,$scope) = split("\0",$chosen);  
         ($launchsymb,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);  
     }  
     if ($launchsymb ne '') {  
         $request->print(&launcher_info_box($launchsymb,$appname,$setter,$linkuri,$scope));  
     }  
     my $error;  
     if ($perm{'mgr'}) {  
         if ($launchsymb 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 $pbmap;  
                             if ($launchsymb =~ /\.(page|sequence)$/) {  
                                 $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($launchsymb))[2]);  
                             } else {  
                                 $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($launchsymb))[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($launchsymb);  
                                         if (ref($res)) {  
                                             my $partlist = $res->parts();  
                                             if (ref($partlist) eq 'ARRAY') {  
                                                 my %record = &Apache::lonnet::restore($launchsymb,$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",$launchsymb,$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,$usec,$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::store_userdata({'score' => $score},$chosen,$namespace,$udom,$uname,$pb{'ip'});  
                                             $ctr++;  
                                             if ($ctr%2 ==1) {  
                                                 $outcome .= &Apache::loncommon::start_data_table_row();  
                                             }  
                                             my $group = $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];  
                                             $outcome .= '<td align="right">'.$ctr.'&nbsp;</td>'.  
                                                         '<td>'.&nameUserString(undef,$$fullname{$student},$uname,$udom).  
                                                         '&nbsp;'.$usec.($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 $key = &Time::HiRes::time().':'.$uname.':'.$udom.':'.  
                                                   "$linkuri\0$linkprotector\0$scope";  
                                         my $ltigrade = {  
                                                          $key => {  
                                                                    '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'   => $launchsymb,  
                                                                    '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)';  
             }  
         }  
     }  
     my $launchsymb = &Apache::loncommon::symb_from_tinyurl($linkuri,$cnum,$cdom);  
     if ($launchsymb eq '') {  
         my %passback = &Apache::lonnet::dump('nohist_linkprot_passback',$cdom,$cnum);  
         foreach my $poss_symb (keys(%passback)) {  
             if (ref($passback{$poss_symb}) eq 'HASH') {  
                 if (exists($passback{$poss_symb}{$chosen})) {  
                     $launchsymb = $poss_symb;  
                     last;  
                 }  
             }  
         }  
         if ($launchsymb ne '') {  
             return ($launchsymb,$appname,$setter);  
         }  
     } else {  
         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} = $symb;  
                 }  
             }  
         }  
         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} = $mapsymb;  
                             }  
                         }  
                     }  
                     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} = $key;  
                                 }  
                             }  
                         }  
                     }  
                 }  
             }  
         }  
     }  
     return %needpb;  
 }  
   
 sub process_passbacks {  
     my ($context,$symbs,$cdom,$cnum,$udom,$uname,$usec,$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'} = $needpb->{$launcher};  
                             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,  
                                         'usec'       => $usec,  
                                         '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,$usec,$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::store_userdata({'score' => $score},$launcher,$namespace,$udom,$uname,$pb{'ip'});  
                         } 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 
 #    on the problem page.  #    on the problem page.
 sub listStudents {  sub listStudents {
     my ($request,$symb,$submitonly,$divforres) = @_;      my ($request,$symb,$submitonly) = @_;
   
     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'};
     my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};      my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
     unless ($submitonly) {      unless ($submitonly) {
         $submitonly = $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};         $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
     }      }
   
     my $result='';      my $result='';
     my $res_error;      my $res_error;
     my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,\$res_error);      my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
   
     my $table;  
     if (ref($partlist) eq 'ARRAY') {  
         if (scalar(@$partlist) > 1 ) {  
             $table = &showResourceInfo($symb,$partlist,$responseType,'gradesub',1);  
         } elsif ($divforres) {  
             $table = '<div style="padding:0;clear:both;margin:0;border:0"></div>';  
         } else {  
             $table = '<br clear="all" />';  
         }  
     }  
   
     $request->print(&checkselect_js());      my %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);
     $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 1977  LISTJAVASCRIPT Line 904  LISTJAVASCRIPT
     $request->print($result);      $request->print($result);
   
     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.      my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.
  "\n".$table;   "\n";
   
     $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();      $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();
     unless ($is_tool) {      $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
         $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))                    .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"
                       .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"                    .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"
                       .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"                    .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"
                       .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"                    .&Apache::lonhtmlcommon::row_closure();
                       .&Apache::lonhtmlcommon::row_closure();      $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))
         $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))                    .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"
                       .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"                    .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n"
                       .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </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="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"                    .&Apache::lonhtmlcommon::row_closure();
                       .&Apache::lonhtmlcommon::row_closure();  
     }  
   
       my $submission_options;
     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;      $submission_options.=
     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',  
                           last     => 'last submission with details',  
                           datesub  => 'all submissions',  
                           all      => 'all submissions with details',  
                       );  
     }  
     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" /> '.
         $optiontext{'lastonly'}.' </label></span>'."\n".          &mt('last submission').' </label></span>'."\n".
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="last" /> '.          '<label><input type="radio" name="lastSub" value="last" /> '.
         $optiontext{'last'}.' </label></span>'."\n".          &mt('last submission with details').' </label></span>'."\n".
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.          '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.
         $optiontext{'datesub'}.'</label></span>'."\n".          &mt('all submissions').'</label></span>'."\n".
         '<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>';          &mt('all submissions with details').'</label></span>';
     my $viewtitle;      $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Submissions'))
     if ($is_tool) {                    .$submission_options
         $viewtitle = &mt('View Transactions');  
     } else {  
         $viewtitle = &mt('View Submissions');  
     }  
     my ($compmsg,$nocompmsg);  
     $nocompmsg = ' checked="checked"';  
     if ($numessay) {  
         $compmsg = $nocompmsg;  
         $nocompmsg = '';  
     }  
     $gradeTable .= &Apache::lonhtmlcommon::row_title($viewtitle)  
                   .$submission_options;  
 # Check if any gradable  
     my $showmore;  
     if ($perm{'mgr'}) {  
         my @sections;  
         if ($env{'request.course.sec'} ne '') {  
             @sections = ($env{'request.course.sec'});  
         } elsif ($env{'form.section'} eq '') {  
             @sections = ('all');  
         } else {  
             @sections = &Apache::loncommon::get_env_multiple('form.section');  
         }  
         if (grep(/^all$/,@sections)) {  
             $showmore = 1;  
         } else {  
             foreach my $sec (@sections) {  
                 if (&canmodify($sec)) {  
                     $showmore = 1;  
                     last;  
                 }  
             }  
         }  
     }  
   
     if ($showmore) {  
         $gradeTable .=  
                    &Apache::lonhtmlcommon::row_closure()  
                   .&Apache::lonhtmlcommon::row_title(&mt('Send Messages'))  
                   .'<span class="LC_nobreak">'  
                   .'<label><input type="radio" name="compmsg" value="0"'.$nocompmsg.' />'  
                   .&mt('No').('&nbsp;'x2).'</label>'  
                   .'<label><input type="radio" name="compmsg" value="1"'.$compmsg.' />'  
                   .&mt('Yes').('&nbsp;'x2).'</label>'  
                   .&Apache::lonhtmlcommon::row_closure();                    .&Apache::lonhtmlcommon::row_closure();
   
         $gradeTable .=       $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments'))
                    &Apache::lonhtmlcommon::row_title(&mt('Grading Increments'))  
                   .'<select name="increment">'                    .'<select name="increment">'
                   .'<option value="1">'.&mt('Whole Points').'</option>'                    .'<option value="1">'.&mt('Whole Points').'</option>'
                   .'<option value=".5">'.&mt('Half Points').'</option>'                    .'<option value=".5">'.&mt('Half Points').'</option>'
                   .'<option value=".25">'.&mt('Quarter Points').'</option>'                    .'<option value=".25">'.&mt('Quarter Points').'</option>'
                   .'<option value=".1">'.&mt('Tenths of a Point').'</option>'                    .'<option value=".1">'.&mt('Tenths of a Point').'</option>'
                   .'</select>';                    .'</select>'
     }                    .&Apache::lonhtmlcommon::row_closure();
   
     $gradeTable .=       $gradeTable .= 
         &build_section_inputs().          &build_section_inputs().
  '<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".   '<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\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".
  '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";   '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";
   
     if (exists($env{'form.Status'})) {      if (exists($env{'form.Status'})) {
  $gradeTable .= '<input type="hidden" name="Status" value="'.$env{'form.Status'}.'" />'."\n";   $gradeTable .= '<input type="hidden" name="Status" value="'.$stu_status.'" />'."\n";
     } else {      } else {
         $gradeTable .= &Apache::lonhtmlcommon::row_closure()          $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status'))
                       .&Apache::lonhtmlcommon::row_title(&mt('Student Status'))  
                       .&Apache::lonhtmlcommon::StatusOptions(                        .&Apache::lonhtmlcommon::StatusOptions(
                            $saveStatus,undef,1,'javascript:reLoadList(this.form);');                             $saveStatus,undef,1,'javascript:reLoadList(this.form);')
     }                        .&Apache::lonhtmlcommon::row_closure();
     if ($numessay) {  
         $gradeTable .= &Apache::lonhtmlcommon::row_closure()  
                       .&Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))  
                       .'<input type="checkbox" name="checkPlag" checked="checked" />';  
     }      }
     $gradeTable .= &Apache::lonhtmlcommon::row_closure(1)  
       $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))
                     .'<input type="checkbox" name="checkPlag" checked="checked" />'
                     .&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>'
                   .$regrademsg."\n"                    .&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"
                   .'<input type="hidden" name="command" value="processGroup" />'                    .'<input type="hidden" name="command" value="processGroup" />'
                   .'</p>';                    .'</p>';
   
Line 2252  LISTJAVASCRIPT Line 1112  LISTJAVASCRIPT
     return '';      return '';
 }  }
   
 #---- Called from the listStudents and the names_for_passback routines.  #---- Called from the listStudents routine
   
 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)=@_;
     my $chkallscript = &Apache::lonhtmlcommon::scripttag('      my $chkallscript= &Apache::lonhtmlcommon::scripttag('
     function checkall() {      function checkall() {
         for (i=0; i<document.forms.'.$form.'.elements.length; i++) {          for (i=0; i<document.forms.'.$form.'.elements.length; i++) {
             ele = document.forms.'.$form.'.elements[i];              ele = document.forms.'.$form.'.elements[i];
Line 2349  sub check_buttons { Line 1161  sub check_buttons {
   
 #     Displays the submissions for one student or a group of students  #     Displays the submissions for one student or a group of students
 sub processGroup {  sub processGroup {
     my ($request,$symb) = @_;      my ($request,$symb)  = @_;
     my $ctr        = 0;      my $ctr        = 0;
     my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');      my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');
     my $total      = scalar(@stuchecked)-1;      my $total      = scalar(@stuchecked)-1;
Line 2537  sub sub_page_js { Line 1349  sub sub_page_js {
 SUBJAVASCRIPT  SUBJAVASCRIPT
 }  }
   
 #--- javascript for grading message center  #--- javascript for essay type problem --
 sub sub_grademessage_js {  sub sub_page_kw_js {
     my $request = shift;      my $request = shift;
     my $iconpath = $request->dir_config('lonIconsURL');      my $iconpath = $request->dir_config('lonIconsURL');
     &commonJSfunctions($request);      &commonJSfunctions($request);
Line 2584  sub sub_grademessage_js { Line 1396  sub sub_grademessage_js {
 </script>  </script>
 INNERJS  INNERJS
   
     my $start_page_msg_central =      my $inner_js_highlight_central= (<<INNERJS);
   <script type="text/javascript">
       function updateChoice(flag) {
         opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);
         opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);
         opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);
         opener.document.SCORE.refresh.value = "on";
         if (opener.document.SCORE.keywords.value!=""){
            opener.document.SCORE.submit();
         }
         self.close()
       }
   </script>
   INNERJS
   
       my $start_page_msg_central = 
         &Apache::loncommon::start_page('Message Central',$inner_js_msg_central,          &Apache::loncommon::start_page('Message Central',$inner_js_msg_central,
        {'js_ready'  => 1,         {'js_ready'  => 1,
  'only_body' => 1,   'only_body' => 1,
  'bgcolor'   =>'#FFFFFF',});   'bgcolor'   =>'#FFFFFF',});
     my $end_page_msg_central =      my $end_page_msg_central = 
    &Apache::loncommon::end_page({'js_ready' => 1});
   
   
       my $start_page_highlight_central = 
           &Apache::loncommon::start_page('Highlight Central',
          $inner_js_highlight_central,
          {'js_ready'  => 1,
    'only_body' => 1,
    'bgcolor'   =>'#FFFFFF',});
       my $end_page_highlight_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\.//;
       my %js_lt = &Apache::lonlocal::texthash(
                   keyw => 'Keywords list, separated by a space. Add/delete to list if desired.',
                   plse => 'Please select a word or group of words from document and then click this link.',
                   adds => 'Add selection to keyword list? Edit if desired.',
                   col1 => 'red',
                   col2 => 'green',
                   col3 => 'blue',
                   siz1 => 'normal',
                   siz2 => '+1',
                   siz3 => '+2',
                   sty1 => 'normal',
                   sty2 => 'italic',
                   sty3 => 'bold',
                );
     my %html_js_lt = &Apache::lonlocal::texthash(      my %html_js_lt = &Apache::lonlocal::texthash(
                 comp => 'Compose Message for: ',                  comp => 'Compose Message for: ',
                 incl => 'Include',                  incl => 'Include',
Line 2604  INNERJS Line 1454  INNERJS
                 new  => 'New',                  new  => 'New',
                 save => 'Save',                  save => 'Save',
                 canc => 'Cancel',                  canc => 'Cancel',
                   kehi => 'Keyword Highlight Options',
                   txtc => 'Text Color',
                   font => 'Font Size',
                   fnst => 'Font Style',
              );               );
       &js_escape(\%js_lt);
     &html_escape(\%html_js_lt);      &html_escape(\%html_js_lt);
     &js_escape(\%html_js_lt);      &js_escape(\%html_js_lt);
     $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));      $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));
   
   //===================== Show list of keywords ====================
     function keywords(formname) {
       var nret = prompt("$js_lt{'keyw'}",formname.keywords.value);
       if (nret==null) return;
       formname.keywords.value = nret;
   
       if (formname.keywords.value != "") {
    formname.refresh.value = "on";
    formname.submit();
       }
       return;
     }
   
 //===================== Script to view submitted by ==================  //===================== Script to view submitted by ==================
   function viewSubmitter(submitter) {    function viewSubmitter(submitter) {
     document.SCORE.refresh.value = "on";      document.SCORE.refresh.value = "on";
Line 2618  INNERJS Line 1486  INNERJS
     return;      return;
   }    }
   
   //===================== Script to add keyword(s) ==================
     function getSel() {
       if (document.getSelection) txt = document.getSelection();
       else if (document.selection) txt = document.selection.createRange().text;
       else return;
       var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
       if (cleantxt=="") {
    alert("$js_lt{'plse'}");
    return;
       }
       var nret = prompt("$js_lt{'adds'}",cleantxt);
       if (nret==null) return;
       document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;
       if (document.SCORE.keywords.value != "") {
    document.SCORE.refresh.value = "on";
    document.SCORE.submit();
       }
       return;
     }
   
 //====================== Script for composing message ==============  //====================== Script for composing message ==============
    // preload images     // preload images
    img1 = new Image();     img1 = new Image();
Line 2719  INNERJS Line 1607  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 2727  INNERJS Line 1616  INNERJS
     pDoc.close();      pDoc.close();
 }  }
   
 SUBJAVASCRIPT  
 }  
   
 #--- javascript for essay type problem --  
 sub sub_page_kw_js {  
     my $request = shift;  
   
     unless ($env{'form.compmsg'}) {  
         &commonJSfunctions($request);  
     }  
   
     my $inner_js_highlight_central= (<<INNERJS);  
 <script type="text/javascript">  
     function updateChoice(flag) {  
       opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);  
       opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);  
       opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);  
       opener.document.SCORE.refresh.value = "on";  
       if (opener.document.SCORE.keywords.value!=""){  
          opener.document.SCORE.submit();  
       }  
       self.close()  
     }  
 </script>  
 INNERJS  
   
     my $start_page_highlight_central =  
         &Apache::loncommon::start_page('Highlight Central',  
                                        $inner_js_highlight_central,  
                                        {'js_ready'  => 1,  
                                         'only_body' => 1,  
                                         'bgcolor'   =>'#FFFFFF',});  
     my $end_page_highlight_central =  
         &Apache::loncommon::end_page({'js_ready' => 1});  
   
     my $docopen=&Apache::lonhtmlcommon::javascript_docopen();  
     $docopen=~s/^document\.//;  
   
     my %js_lt = &Apache::lonlocal::texthash(  
                 keyw => 'Keywords list, separated by a space. Add/delete to list if desired.',  
                 plse => 'Please select a word or group of words from document and then click this link.',  
                 adds => 'Add selection to keyword list? Edit if desired.',  
                 col1 => 'red',  
                 col2 => 'green',  
                 col3 => 'blue',  
                 siz1 => 'normal',  
                 siz2 => '+1',  
                 siz3 => '+2',  
                 sty1 => 'normal',  
                 sty2 => 'italic',  
                 sty3 => 'bold',  
              );  
     my %html_js_lt = &Apache::lonlocal::texthash(  
                 save => 'Save',  
                 canc => 'Cancel',  
                 kehi => 'Keyword Highlight Options',  
                 txtc => 'Text Color',  
                 font => 'Font Size',  
                 fnst => 'Font Style',  
              );  
     &js_escape(\%js_lt);  
     &html_escape(\%html_js_lt);  
     &js_escape(\%html_js_lt);  
     $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));  
   
 //===================== Show list of keywords ====================  
   function keywords(formname) {  
     var nret = prompt("$js_lt{'keyw'}",formname.keywords.value);  
     if (nret==null) return;  
     formname.keywords.value = nret;  
   
     if (formname.keywords.value != "") {  
         formname.refresh.value = "on";  
         formname.submit();  
     }  
     return;  
   }  
   
 //===================== Script to add keyword(s) ==================  
   function getSel() {  
     if (document.getSelection) txt = document.getSelection();  
     else if (document.selection) txt = document.selection.createRange().text;  
     else return;  
     if (typeof(txt) != 'string') {  
         txt = String(txt);  
     }  
     var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");  
     if (cleantxt=="") {  
         alert("$js_lt{'plse'}");  
         return;  
     }  
     var nret = prompt("$js_lt{'adds'}",cleantxt);  
     if (nret==null) return;  
     document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;  
     if (document.SCORE.keywords.value != "") {  
         document.SCORE.refresh.value = "on";  
         document.SCORE.submit();  
     }  
     return;  
   }  
   
 //====================== Script for keyword highlight options ==============  //====================== Script for keyword highlight options ==============
   function kwhighlight() {    function kwhighlight() {
     var kwclr    = document.SCORE.kwclr.value;      var kwclr    = document.SCORE.kwclr.value;
Line 3018  sub gradeBox { Line 1806  sub gradeBox {
   
 sub handback_box {  sub handback_box {
     my ($symb,$uname,$udom,$counter,$partid,$record,$res_error_pointer) = @_;      my ($symb,$uname,$udom,$counter,$partid,$record,$res_error_pointer) = @_;
     my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,$res_error_pointer);      my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error_pointer);
     return unless ($numessay);  
     my (@respids);      my (@respids);
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id = &flatten_responseType($responseType);
     foreach my $part_response_id (@part_response_id) {      foreach my $part_response_id (@part_response_id) {
Line 3038  sub handback_box { Line 1825  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 3121  sub show_problem { Line 1908  sub show_problem {
 sub files_exist {  sub files_exist {
     my ($r, $symb) = @_;      my ($r, $symb) = @_;
     my @students = &Apache::loncommon::get_env_multiple('form.stuinfo');      my @students = &Apache::loncommon::get_env_multiple('form.stuinfo');
   
     foreach my $student (@students) {      foreach my $student (@students) {
         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)= &get_last_submission(\%record);          my ($string,$timestamp)= &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 3140  sub files_exist { Line 1928  sub files_exist {
 sub download_all_link {  sub download_all_link {
     my ($r,$symb) = @_;      my ($r,$symb) = @_;
     unless (&files_exist($r, $symb)) {      unless (&files_exist($r, $symb)) {
         $r->print(&mt('There are currently no submitted documents.'));         $r->print(&mt('There are currently no submitted documents.'));
         return;         return;
     }      }
   
     my $all_students =       my $all_students = 
  join("\n", &Apache::loncommon::get_env_multiple('form.stuinfo'));   join("\n", &Apache::loncommon::get_env_multiple('form.stuinfo'));
   
Line 3161  sub download_all_link { Line 1950  sub download_all_link {
 sub submit_download_link {  sub submit_download_link {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
     if (!$symb) { return ''; }      if (!$symb) { return ''; }
     my $res_error;  #FIXME: Figure out which type of problem this is and provide appropriate download
     my ($partlist,$handgrade,$responseType,$numresp,$numessay,$numdropbox) =      &download_all_link($request,$symb);
         &response_type($symb,\$res_error);  
     if ($res_error) {  
         $request->print(&mt('An error occurred retrieving response types'));  
         return;  
     }  
     unless ($numessay) {  
         $request->print(&mt('No essayresponse items found'));  
         return;  
     }  
     my @chosenparts = &Apache::loncommon::get_env_multiple('form.vPart');  
     if (@chosenparts) {  
         $request->print(&showResourceInfo($symb,$partlist,$responseType,  
                                           undef,undef,1));  
     }  
     if ($numessay) {  
         my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};  
         my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};  
         my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};  
         (undef,undef,my $fullname) = &getclasslist($getsec,1,$getgroup,$symb,$submitonly,1);  
         if (ref($fullname) eq 'HASH') {  
             my @students = map { $_.':'.$fullname->{$_} } (keys(%{$fullname}));  
             if (@students) {  
                 @{$env{'form.stuinfo'}} = @students;  
                 if ($numdropbox) {  
                     &download_all_link($request,$symb);  
                 } else {  
                     $request->print(&mt('No essayrespose items with dropbox found'));  
                 }  
 # FIXME Need a mechanism to download essays, i.e., if $numessay > $numdropbox  
 # Needs to omit user's identity if resource instance is for an anonymous survey.  
             } else {  
                 $request->print(&mt('No students match the criteria you selected'));  
             }  
         } else {  
             $request->print(&mt('Could not retrieve student information'));  
         }  
     } else {  
         $request->print(&mt('No essayresponse items found'));  
     }  
     return;  
 }  }
   
 sub build_section_inputs {  sub build_section_inputs {
Line 3220  sub build_section_inputs { Line 1969  sub build_section_inputs {
   
 # --------------------------- show submissions of a student, option to grade   # --------------------------- show submissions of a student, option to grade 
 sub submission {  sub submission {
     my ($request,$counter,$total,$symb,$divforres,$calledby) = @_;      my ($request,$counter,$total,$symb) = @_;
     my ($uname,$udom)     = ($env{'form.student'},$env{'form.userdom'});      my ($uname,$udom)     = ($env{'form.student'},$env{'form.userdom'});
     $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?      $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?
     my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});      my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});
     $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';      $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';
   
       my $probtitle=&Apache::lonnet::gettitle($symb); 
     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 $is_tool = ($symb =~ /ext\.tool$/);  
     my ($essayurl,%coursedesc_by_cid);  
   
     if (!&canview($usec)) {      if (!&canview($usec)) {
         $request->print(          $request->print(
Line 3241  sub submission { Line 1988  sub submission {
  return;   return;
     }      }
   
     my $res_error;  
     my ($partlist,$handgrade,$responseType,$numresp,$numessay) =  
         &response_type($symb,\$res_error);  
     if ($res_error) {  
         $request->print(&navmap_errormsg());  
         return;  
     }  
   
     if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }      if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }
     unless ($is_tool) {      if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }
         if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }      if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }
         if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }  
     }  
     if (($numessay) && ($calledby eq 'submission') && (!exists($env{'form.compmsg'}))) {  
         $env{'form.compmsg'} = 1;  
     }  
     my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');      my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
     my $checkIcon = '<img alt="'.&mt('Check Mark').      my $checkIcon = '<img alt="'.&mt('Check Mark').
  '" src="'.$request->dir_config('lonIconsURL').   '" src="'.$request->dir_config('lonIconsURL').
Line 3264  sub submission { Line 1998  sub submission {
   
     # header info      # header info
     if ($counter == 0) {      if ($counter == 0) {
         my @chosenparts = &Apache::loncommon::get_env_multiple('form.vPart');  
         if (@chosenparts) {  
             $request->print(&showResourceInfo($symb,$partlist,$responseType,'gradesub'));  
         } elsif ($divforres) {  
             $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');  
         } else {  
             $request->print('<br clear="all" />');  
         }  
  &sub_page_js($request);   &sub_page_js($request);
         &sub_grademessage_js($request) if ($env{'form.compmsg'});   &sub_page_kw_js($request);
  &sub_page_kw_js($request) if ($numessay);  
   
  # option to display problem, only once else it cause problems    # option to display problem, only once else it cause problems 
         # with the form later since the problem has a form.          # with the form later since the problem has a form.
Line 3291  sub submission { Line 2016  sub submission {
     $request->print(&show_problem($request,$symb,$uname,$udom,0,1,$mode));      $request->print(&show_problem($request,$symb,$uname,$udom,0,1,$mode));
  }   }
   
    # kwclr is the only variable that is guaranteed not to be blank 
           # if this subroutine has been called once.
  my %keyhash = ();   my %keyhash = ();
  if (($env{'form.kwclr'} eq '' && $numessay) || ($env{'form.compmsg'})) {  # if ($env{'form.kwclr'} eq '' && $env{'form.handgrade'} eq 'yes') {
           if (1) {
     %keyhash = &Apache::lonnet::dump('nohist_handgrade',      %keyhash = &Apache::lonnet::dump('nohist_handgrade',
      $env{'course.'.$env{'request.course.id'}.'.domain'},       $env{'course.'.$env{'request.course.id'}.'.domain'},
      $env{'course.'.$env{'request.course.id'}.'.num'});       $env{'course.'.$env{'request.course.id'}.'.num'});
  }  
  # kwclr is the only variable that is guaranteed not to be blank  
  # if this subroutine has been called once.  
  if ($env{'form.kwclr'} eq '' && $numessay) {  
     my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};      my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
     $env{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';      $env{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
     $env{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';      $env{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
     $env{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';      $env{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
     $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';      $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
  }      $env{'form.msgsub'}   = $keyhash{$symb.'_subject'} ne '' ? 
  if ($env{'form.compmsg'}) {  
     $env{'form.msgsub'}   = $keyhash{$symb.'_subject'} ne '' ?  
  $keyhash{$symb.'_subject'} : $probtitle;   $keyhash{$symb.'_subject'} : $probtitle;
     $env{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';      $env{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';
  }   }
   
  my $overRideScore = $env{'form.overRideScore'} eq '' ? 'no' : $env{'form.overRideScore'};   my $overRideScore = $env{'form.overRideScore'} eq '' ? 'no' : $env{'form.overRideScore'};
  my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));   my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
  $request->print('<form action="/adm/grades" method="post" name="SCORE" enctype="multipart/form-data">'."\n".   $request->print('<form action="/adm/grades" method="post" name="SCORE" enctype="multipart/form-data">'."\n".
Line 3325  sub submission { Line 2047  sub submission {
  '<input type="hidden" name="vProb"      value="'.$env{'form.vProb'}.'" />'."\n".   '<input type="hidden" name="vProb"      value="'.$env{'form.vProb'}.'" />'."\n".
  '<input type="hidden" name="vAns"       value="'.$env{'form.vAns'}.'" />'."\n".   '<input type="hidden" name="vAns"       value="'.$env{'form.vAns'}.'" />'."\n".
  '<input type="hidden" name="lastSub"    value="'.$env{'form.lastSub'}.'" />'."\n".   '<input type="hidden" name="lastSub"    value="'.$env{'form.lastSub'}.'" />'."\n".
  '<input type="hidden" name="compmsg"    value="'.$env{'form.compmsg'}.'" />'."\n".  
  &build_section_inputs().   &build_section_inputs().
  '<input type="hidden" name="submitonly" value="'.$env{'form.submitonly'}.'" />'."\n".   '<input type="hidden" name="submitonly" value="'.$env{'form.submitonly'}.'" />'."\n".
  '<input type="hidden" name="NCT"'.   '<input type="hidden" name="NCT"'.
  ' value="'.($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : $total+1).'" />'."\n");   ' value="'.($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : $total+1).'" />'."\n");
  if ($env{'form.compmsg'}) {  # if ($env{'form.handgrade'} eq 'yes') {
     $request->print('<input type="hidden" name="msgsub"   value="'.$env{'form.msgsub'}.'" />'."\n".          if (1) {
     '<input type="hidden" name="shownSub" value="0" />'."\n".  
     '<input type="hidden" name="savemsgN" value="'.$env{'form.savemsgN'}.'" />'."\n");  
  }  
  if ($numessay) {  
     $request->print('<input type="hidden" name="keywords" value="'.$env{'form.keywords'}.'" />'."\n".      $request->print('<input type="hidden" name="keywords" value="'.$env{'form.keywords'}.'" />'."\n".
     '<input type="hidden" name="kwclr"    value="'.$env{'form.kwclr'}.'" />'."\n".      '<input type="hidden" name="kwclr"    value="'.$env{'form.kwclr'}.'" />'."\n".
     '<input type="hidden" name="kwsize"   value="'.$env{'form.kwsize'}.'" />'."\n".      '<input type="hidden" name="kwsize"   value="'.$env{'form.kwsize'}.'" />'."\n".
     '<input type="hidden" name="kwstyle"  value="'.$env{'form.kwstyle'}.'" />'."\n");      '<input type="hidden" name="kwstyle"  value="'.$env{'form.kwstyle'}.'" />'."\n".
       '<input type="hidden" name="msgsub"   value="'.$env{'form.msgsub'}.'" />'."\n".
       '<input type="hidden" name="shownSub" value="0" />'."\n".
       '<input type="hidden" name="savemsgN" value="'.$env{'form.savemsgN'}.'" />'."\n");
       foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {
    $request->print('<input type="hidden" name="vPart" value="'.$partid.'" />'."\n");
       }
  }   }
   
  my ($cts,$prnmsg) = (1,'');   my ($cts,$prnmsg) = (1,'');
  while ($cts <= $env{'form.savemsgN'}) {   while ($cts <= $env{'form.savemsgN'}) {
     $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.      $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.
Line 3354  sub submission { Line 2077  sub submission {
  }   }
  $request->print($prnmsg);   $request->print($prnmsg);
   
  if ($numessay) {  # if ($env{'form.handgrade'} eq 'yes') {
           if (1) {
   
             my %lt = &Apache::lonlocal::texthash(              my %lt = &Apache::lonlocal::texthash(
                           keyh => 'Keyword Highlighting for Essays',                            keyh => 'Keyword Highlighting for Essays',
Line 3362  sub submission { Line 2086  sub submission {
                           list => 'List',                            list => 'List',
                           past => 'Paste Selection to List',                            past => 'Paste Selection to List',
                           high => 'Highlight Attribute',                            high => 'Highlight Attribute',
                      );                       );    
 #  #
 # Print out the keyword options line  # Print out the keyword options line
 #  #
Line 3381  sub submission { Line 2105  sub submission {
 #  #
 # Load the other essays for similarity check  # Load the other essays for similarity check
 #  #
             (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);              my (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);
             if ($essayurl eq 'lib/templates/simpleproblem.problem') {      my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);
                 my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};      $apath=&escape($apath);
                 my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};      $apath=~s/\W/\_/gs;
                 if ($cdom ne '' && $cnum ne '') {              &init_old_essays($symb,$apath,$adom,$aname);
                     my ($map,$id,$res) = &Apache::lonnet::decode_symb($symb);  
                     if ($map =~ m{^\Quploaded/$cdom/$cnum/\E(default(?:|_\d+)\.(?:sequence|page))$}) {  
                         my $apath = $1.'_'.$id;  
                         $apath=~s/\W/\_/gs;  
                         &init_old_essays($symb,$apath,$cdom,$cnum);  
                     }  
                 }  
             } else {  
         my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);  
         $apath=&escape($apath);  
         $apath=~s/\W/\_/gs;  
                 &init_old_essays($symb,$apath,$adom,$aname);  
             }  
         }          }
     }      }
   
Line 3442  sub submission { Line 2153  sub submission {
     }      }
   
     my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);      my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
       my $res_error;
       my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print(&navmap_errormsg());
           return;
       }
   
     # 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">'.$boxtitle.'</h3>';                .'<h3 class="LC_hcell">'.&mt('Submissions').'</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) && !$is_tool) {  #    if ($env{'form.handgrade'} eq 'no') {
       if (1) {
         $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";
     }      }
   
     # If any part of the problem is an essayresponse, then check for collaborators      # If any part of the problem is an essay-response (handgraded), then check for collaborators
     my $fullname;      my $fullname;
     my $col_fullnames = [];      my $col_fullnames = [];
     if ($numessay) {  #    if ($env{'form.handgrade'} eq 'yes') {
       if (1) {
  (my $sub_result,$fullname,$col_fullnames)=   (my $sub_result,$fullname,$col_fullnames)=
     &check_collaborators($symb,$uname,$udom,\%record,$handgrade,      &check_collaborators($symb,$uname,$udom,\%record,$handgrade,
  $counter);   $counter);
  $result.=$sub_result;   $result.=$sub_result;
     }      }
     $request->print($result."\n");      $request->print($result."\n");
       
     # print student answer/submission      # print student answer/submission
     # Options are (1) Last submission only      # Options are (1) Handgraded submission only
     #             (2) Last submission (with detailed information for that submission)      #             (2) Last submission, includes submission that is not handgraded 
     #             (3) All transactions (by date)      #                  (for multi-response type part)
     #             (4) The whole record (with detailed information for all transactions)      #             (3) Last submission plus the parts info
       #             (4) The whole record for this student
     my ($lastsubonly,$partinfo) =      
         &show_last_submission($uname,$udom,$symb,$essayurl,$responseType,$env{'form.lastSub'},      my ($string,$timestamp)= &get_last_submission(\%record);
                               $is_tool,$fullname,\%record,\%coursedesc_by_cid);  
     $request->print($partinfo);      my $lastsubonly;
     $request->print($lastsubonly);  
       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) {
       next if ($env{'form.lastSub'} eq 'hdgrade' 
    && $$handgrade{$$part[0].'_'.$$part[1]} ne 'yes');
   
       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);
    my %old_course_desc = 
       &Apache::lonnet::coursedescription($ocrsid,
    {'one_time' => 1});
   
                           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 /><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><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)$/ || ($env{'form.lastSub'} eq 'hdgrade' && 
    $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) {
       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);
     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 3499  sub submission { Line 2344  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");
     }      }
   
     # grading message center      # essay grading message center
   #    if ($env{'form.handgrade'} eq 'yes') {
     if ($env{'form.compmsg'}) {      if (1) {
         my $result='<div class="LC_Box">'.   my $result='<div class="LC_grade_message_center">';
                    '<h3 class="LC_hcell">'.&mt('Send Message').'</h3>'.      
                    '<div class="LC_grade_message_center_body">';   $result.='<div class="LC_grade_message_center_header">'.
         my ($lastname,$givenn) = split(/,/,$env{'form.fullname'});      &mt('Send Message').'</div><div class="LC_grade_message_center_body">';
         my $msgfor = $givenn.' '.$lastname;   my ($lastname,$givenn) = split(/,/,$env{'form.fullname'});
         if (scalar(@$col_fullnames) > 0) {   my $msgfor = $givenn.' '.$lastname;
             my $lastone = pop(@$col_fullnames);   if (scalar(@$col_fullnames) > 0) {
             $msgfor .= ', '.(join ', ',@$col_fullnames).' and '.$lastone.'.';      my $lastone = pop(@$col_fullnames);
         }      $msgfor .= ', '.(join ', ',@$col_fullnames).' and '.$lastone.'.';
         $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript   }
         $result.='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".   $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript
                  '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".   $result.='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
          '&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.      '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";
                  ',\''.$msgfor.'\');" target="_self">'.   $result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
                  &mt('Compose message to student'.(scalar(@$col_fullnames) >= 1 ? 's' : '')).'</a><label> ('.      ',\''.$msgfor.'\');" target="_self">'.
                  &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.      &mt('Compose message to student'.(scalar(@$col_fullnames) >= 1 ? 's' : '')).'</a><label> ('.
                  ' <img src="'.$request->dir_config('lonIconsURL').      &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.
                  '/mailbkgrd.gif" width="14" height="10" alt="" name="mailicon'.$counter.'" />'."\n".      ' <img src="'.$request->dir_config('lonIconsURL').
                  '<br />&nbsp;('.      '/mailbkgrd.gif" width="14" height="10" alt="" name="mailicon'.$counter.'" />'."\n".
                  &mt('Message will be sent when you click on Save &amp; Next below.').")\n".      '<br />&nbsp;('.
          '</div></div>';      &mt('Message will be sent when you click on Save &amp; Next below.').")\n";
         $request->print($result);   $result.='</div></div>';
    $request->print($result);
     }      }
   
     my %seen = ();      my %seen = ();
     my @partlist;      my @partlist;
     my @gradePartRespid;      my @gradePartRespid;
     my @part_response_id;      my @part_response_id = &flatten_responseType($responseType);
     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 3551  sub submission { Line 2392  sub submission {
  my $part_resp = join('_',@{ $part_response_id });   my $part_resp = join('_',@{ $part_response_id });
  next if ($seen{$partid} > 0);   next if ($seen{$partid} > 0);
  $seen{$partid}++;   $seen{$partid}++;
    next if ($$handgrade{$part_resp} ne 'yes' 
    && $env{'form.lastSub'} eq 'hdgrade');
  push(@partlist,$partid);   push(@partlist,$partid);
  push(@gradePartRespid,$partid.'.'.$respid);   push(@gradePartRespid,$partid.'.'.$respid);
  $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));   $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
Line 3606  sub submission { Line 2449  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 3844  sub check_collaborators { Line 2507  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,$is_tool)=@_;      my ($returnhash)=@_;
     my (@string,$timestamp,$lastgradetime,$lastsubmittime);      my (@string,$timestamp,%lasthidden);
     if ($$returnhash{'version'}) {      if ($$returnhash{'version'}) {
  my %lasthash=();   my %lasthash=();
         my %prevsolved=();   my ($version);
         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};
                 if ($key =~ /\.([^.]+)\.regrader$/) {   $timestamp = 
                     $handgraded{$1} = 1;      &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});
                 } 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 3943  sub get_last_submission { Line 2573  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">'.$msg.'</span>';      '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span>';
     }      }
     return (\@string,$timestamp,$lastgradetime,$lastsubmittime);      return (\@string,\$timestamp);
 }  }
   
 #--- High light keywords, with style choosen by user.  #--- High light keywords, with style choosen by user.
Line 3982  sub show_previous_task_version { Line 2606  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 4158  sub processHandGrade { Line 2783  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,undef,undef,\%queueable,\%needpb,\%skip_passback,\%pbsave);                  &saveHandGrade($request,$symb,$uname,$udom,$ctr);
     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 4237  sub processHandGrade { Line 2842  sub processHandGrade {
  foreach my $collabstr (@collabstrs) {   foreach my $collabstr (@collabstrs) {
     my ($part,@collaborators) = split(/:/,$collabstr);      my ($part,@collaborators) = split(/:/,$collabstr);
     foreach my $collaborator (@collaborators) {      foreach my $collaborator (@collaborators) {
  my ($errorflag,$pts,$wgt,$numchg,$numupdate) =    my ($errorflag,$pts,$wgt) = 
     &saveHandGrade($request,$symb,$collaborator,$udom,$ctr,      &saveHandGrade($request,$symb,$collaborator,$udom,$ctr,
    $env{'form.unamedom'.$ctr},$part,\%queueable);     $env{'form.unamedom'.$ctr},$part);
  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;
                         } else {   } elsif ($message ne '') {
                             if ($numchg || $numupdate) {      my ($baseurl,$showsymb) = 
                                 $pbcollab{$collaborator}{$part} = [$pts,$wgt];   &get_feedurl_and_symb($symb,$collaborator,
                             }        $udom);
                             if ($message ne '') {      if ($env{'form.withgrades'.$ctr}) {
                                 my ($baseurl,$showsymb) =   $messagetail = " for <a href=\"".
                                     &get_feedurl_and_symb($symb,$collaborator,                                      $baseurl."?symb=$showsymb\">$restitle</a>";
                                                           $udom);      }
                                 if ($env{'form.withgrades'.$ctr}) {      $msgstatus = 
                                     $messagetail = " for <a href=\"".   &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle);
                                         $baseurl."?symb=$showsymb\">$restitle</a>";  
                                 }  
                                 $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))) {  
             foreach my $user (keys(%pbcollab)) {  
                 my ($clbuname,$clbudom) = split(/:/,$user);  
                 my $clbusec = &Apache::lonnet::getsection($clbudom,$clbuname,$cdom.'_'.$cnum);  
                 if (ref($pbcollab{$user}) eq 'HASH') {  
                     my @clparts = keys(%{$pbcollab{$user}});  
                     if (@clparts) {  
                         my $navmap = Apache::lonnavmaps::navmap->new($clbuname,$clbudom,$clbusec);  
                         if (ref($navmap)) {  
                             my $res = $navmap->getBySymb($symb);  
                             if (ref($res)) {  
                                 my $partlist = $res->parts();  
                                 if (ref($partlist) eq 'ARRAY') {  
                                     my (%weights,%awardeds,%excuseds);  
                                     foreach my $part (@{$partlist}) {  
                                         if ($res->status($part) eq $res->EXCUSED) {  
                                             $excuseds{$symb}{$part} = 1;  
                                         } else {  
                                             $excuseds{$symb}{$part} = '';  
                                         }  
                                         if ((exists($pbcollab{$user}{$part})) && (ref($pbcollab{$user}{$part}) eq 'ARRAY')) {  
                                             my $pts = $pbcollab{$user}{$part}[0];  
                                             my $wt = $pbcollab{$user}{$part}[1];  
                                             if ($wt) {  
                                                 $awardeds{$symb}{$part} = $pts/$wt;  
                                                 $weights{$symb}{$part} = $wt;  
                                             } else {  
                                                 $awardeds{$symb}{$part} = 0;  
                                                 $weights{$symb}{$part} = 0;  
                                             }  
                                         } else {  
                                             $awardeds{$symb}{$part} = $res->awarded($part);  
                                             $weights{$symb}{$part} = $res->weight($part);  
                                         }  
                                     }  
                                     &process_passbacks('handgrade',[$symb],$cdom,$cnum,$clbudom,$clbuname,$clbusec,\%weights,  
                                                        \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave);  
                                 }  
                             }  
                         }  
                     }  
                 }  
             }  
         }  
     }      }
   
     my %keyhash = ();  #    if ($env{'form.handgrade'} eq 'yes') {
     if ($numessay) {      if (1) {
  # Keywords sorted in alphabatical order   # Keywords sorted in alphabatical order
  my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};   my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
    my %keyhash = ();
  $env{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;   $env{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;
  $env{'form.keywords'}           =~ s/^\s+|\s+$//g;   $env{'form.keywords'}           =~ s/^\s+|\s+$//;
  my (@keywords) = sort(split(/\s+/,$env{'form.keywords'}));   my (@keywords) = sort(split(/\s+/,$env{'form.keywords'}));
  $env{'form.keywords'} = join(' ',@keywords);   $env{'form.keywords'} = join(' ',@keywords);
  $keyhash{$symb.'_keywords'}     = $env{'form.keywords'};   $keyhash{$symb.'_keywords'}     = $env{'form.keywords'};
Line 4323  sub processHandGrade { Line 2880  sub processHandGrade {
  $keyhash{$loginuser.'_kwclr'}   = $env{'form.kwclr'};   $keyhash{$loginuser.'_kwclr'}   = $env{'form.kwclr'};
  $keyhash{$loginuser.'_kwsize'}  = $env{'form.kwsize'};   $keyhash{$loginuser.'_kwsize'}  = $env{'form.kwsize'};
  $keyhash{$loginuser.'_kwstyle'} = $env{'form.kwstyle'};   $keyhash{$loginuser.'_kwstyle'} = $env{'form.kwstyle'};
     }  
   
     if ($env{'form.compmsg'}) {  
  # message center - Order of message gets changed. Blank line is eliminated.   # message center - Order of message gets changed. Blank line is eliminated.
  # New messages are saved in env for the next student.   # New messages are saved in env for the next student.
  # All messages are saved in nohist_handgrade.db   # All messages are saved in nohist_handgrade.db
Line 4348  sub processHandGrade { Line 2903  sub processHandGrade {
  }   }
  $env{'form.savemsgN'} = --$idx;   $env{'form.savemsgN'} = --$idx;
  $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};   $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};
     }  
     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
     my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');      my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');
     if ($env{'form.refresh'} eq 'on') {      if ($env{'form.refresh'} eq 'on') {
Line 4393  sub processHandGrade { Line 2945  sub processHandGrade {
  }   }
  return $a cmp $b;   return $a cmp $b;
      } (keys(%$fullname))) {       } (keys(%$fullname))) {
   # FIXME: this is fishy, looks like the button label
  if ($nextflg == 1 && $button =~ /Next$/) {   if ($nextflg == 1 && $button =~ /Next$/) {
     push(@parsedlist,$item);      push(@parsedlist,$item);
  }   }
Line 4403  sub processHandGrade { Line 2956  sub processHandGrade {
  }   }
     }      }
     $ctr = 0;      $ctr = 0;
   # FIXME: this is fishy, looks like the button label
     @parsedlist = reverse @parsedlist if ($button eq 'Previous');      @parsedlist = reverse @parsedlist if ($button eq 'Previous');
       my $res_error;
       my ($partlist) = &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print(&navmap_errormsg());
           return;
       }
     foreach my $student (@parsedlist) {      foreach my $student (@parsedlist) {
  my $submitonly=$env{'form.submitonly'};   my $submitonly=$env{'form.submitonly'};
  my ($uname,$udom) = split(/:/,$student);   my ($uname,$udom) = split(/:/,$student);
Line 4453  sub processHandGrade { Line 3013  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 4461  sub processHandGrade { Line 3021  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,      my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_;
         $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 4470  sub saveHandGrade { Line 3029  sub saveHandGrade {
     my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname);      my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname);
     my @parts_graded;      my @parts_graded;
     my %newrecord  = ();      my %newrecord  = ();
     my ($pts,$wgt,$totchg,$sendupdate,$poss_pb) = ('','',0,0,0);      my ($pts,$wgt,$totchg) = ('','',0);
     my %aggregate = ();      my %aggregate = ();
     my $aggregateflag = 0;      my $aggregateflag = 0;
     if ($env{'form.HIDE'.$newflg}) {      if ($env{'form.HIDE'.$newflg}) {
Line 4478  sub saveHandGrade { Line 3037  sub saveHandGrade {
         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 ((ref($needpb) eq 'HASH') && (keys(%{$needpb}))) {  
         $poss_pb = 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 ($submitter 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 ($poss_pb) {  
             $weights{$symb}{$new_part} =  
                 &Apache::lonnet::EXT('resource.'.$new_part.'.weight',$symb,$udom,$uname);  
         } elsif ($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 ++;  
     }      }
  } 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 4528  sub saveHandGrade { Line 3072  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 ++;  
             $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 4541  sub saveHandGrade { Line 3082  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 ++;  
     }      }
     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 4595  sub saveHandGrade { Line 3133  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,$queueable);       $cdom,$cnum,$domain,$stuname);
     }      }
     if ($aggregateflag) {      if ($aggregateflag) {
         &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,          &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
       $cdom,$cnum);        $cdom,$cnum);
     }      }
     if (($sendupdate || $totchg) && (!$submitter) && ($poss_pb)) {      return ('',$pts,$wgt,$totchg);
         &process_passbacks('handgrade',[$symb],$cdom,$cnum,$domain,$stuname,$usec,\%weights,  
                            \%awardeds,\%excuseds,$needpb,$skip_passback,$pbsave);  
     }  
     return ('',$pts,$wgt,$totchg,$sendupdate);  
 }  }
   
 sub makehidden {  sub makehidden {
Line 4639  sub makehidden { Line 3173  sub makehidden {
 }  }
   
 sub check_and_remove_from_queue {  sub check_and_remove_from_queue {
     my ($parts,$record,$newrecord,$symb,$cdom,$cnum,$domain,$stuname,$queueable) = @_;      my ($parts,$record,$newrecord,$symb,$cdom,$cnum,$domain,$stuname) = @_;
     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 4647  sub check_and_remove_from_queue { Line 3181  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'
  ) {   ) {
             if ($queueable->{$part}) {      push(@ungraded_parts, $part);
         push(@ungraded_parts, $part);  
             }  
  }   }
     }      }
     if ( !@ungraded_parts ) {      if ( !@ungraded_parts ) {
Line 4675  sub handback_files { Line 3207  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 4705  sub handback_files { Line 3237  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 4717  sub handback_files { Line 3248  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 4837  sub version_portfiles { Line 3368  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);      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($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 5103  VIEWJAVASCRIPT Line 3561  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 5142  sub viewgrades { Line 3595  sub viewgrades {
     }      }
   
     my ($common_header,$specific_header,@sections,$section_display);      my ($common_header,$specific_header,@sections,$section_display);
     if ($env{'request.course.sec'} ne '') {      @sections = &Apache::loncommon::get_env_multiple('form.section');
         @sections = ($env{'request.course.sec'});  
     } else {  
         @sections = &Apache::loncommon::get_env_multiple('form.section');  
     }  
   
 # Check if Save button should be usable  
     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)) {      if (grep(/^all$/,@sections)) {
         @sections = ('all');          @sections = ('all');
         if ($group_display) {          if ($group_display) {
Line 5171  sub viewgrades { Line 3605  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 5184  sub viewgrades { Line 3618  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 5198  sub viewgrades { Line 3632  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 5207  sub viewgrades { Line 3641  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 {
         my $text;          $result.= '<h3>'.$common_header.'&nbsp;'.&mt('(submission status: "[_1]")',$submission_status).'</h3>';
         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 5226  sub viewgrades { Line 3654  sub viewgrades {
     my %weight = ();      my %weight = ();
     my $ctsparts = 0;      my $ctsparts = 0;
     my %seen = ();      my %seen = ();
     my @part_response_id;      my @part_response_id = &flatten_responseType($responseType);
     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 });
  next if $seen{$partid};   next if $seen{$partid};
  $seen{$partid}++;   $seen{$partid}++;
    my $handgrade=$$handgrade{$part_resp};
  my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);   my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
  $weight{$partid} = $wgt eq '' ? '1' : $wgt;   $weight{$partid} = $wgt eq '' ? '1' : $wgt;
   
Line 5255  sub viewgrades { Line 3679  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 5285  sub viewgrades { Line 3709  sub viewgrades {
     if ($env{'form.submitonly'} eq 'all') {      if ($env{'form.submitonly'} eq 'all') {
         $result.= '<h3>'.$specific_header.'</h3>';          $result.= '<h3>'.$specific_header.'</h3>';
     } else {      } else {
         my $text;          $result.= '<h3>'.$specific_header.'&nbsp;'.&mt('(submission status: "[_1]")',$submission_status).'</h3>';
         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 5305  sub viewgrades { Line 3723  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',$toolsymb);   my $display=&Apache::lonnet::metadata($url,$part.'.display');
         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',$toolsymb); }   if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
  my ($partid) = &split_part_type($part);   my ($partid) = &split_part_type($part);
         push(@partids,$partid);          push(@partids,$partid);
 #  #
Line 5317  sub viewgrades { Line 3735  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 5349  sub viewgrades { Line 3767  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,$is_tool);     $_,$$fullname{$_},\@parts,\%weight,\$ctr,\%last_resets);
     }      }
     $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";
     $result.='<input type="button" value="'.&mt('Save').'"'.$disabled.' '.      $result.='<input type="button" value="'.&mt('Save').'" '.
  'onclick="javascript:submit();" target="_self" /></form>'."\n";   'onclick="javascript:submit();" target="_self" /></form>'."\n";
     if ($ctr == 0) {      if ($ctr == 0) {
         my $stu_status = join(' or ',&Apache::loncommon::get_env_multiple('form.Status'));          my $stu_status = join(' or ',&Apache::loncommon::get_env_multiple('form.Status'));
Line 5366  sub viewgrades { Line 3784  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 5419  sub viewgrades { Line 3837  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 5429  sub viewgrades { Line 3847  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,$is_tool) = @_;      my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_;
     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 5543  sub viewstudentgrade { Line 3961  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>';
     $title.='<h4><b>'.&mt('Section:').'</b> '.$section_display.'</h4>'."\n";      $title.='<h4>'.&mt('<b>Section: </b>[_1]',$section_display).'</h4>'."\n";
   
     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().
Line 5566  sub editgrades { Line 3980  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 5588  sub editgrades { Line 3998  sub editgrades {
  $ctr++;   $ctr++;
     }      }
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     my $totcolspan = 0;  
     foreach my $partid (@partid) {      foreach my $partid (@partid) {
  $header .= '<th align="center">'.&mt('Old Score').'</th>'.   $header .= '<th align="center">'.&mt('Old Score').'</th>'.
     '<th align="center">'.&mt('New Score').'</th>';      '<th align="center">'.&mt('New Score').'</th>';
Line 5597  sub editgrades { Line 4006  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',$toolsymb);      my $display=&Apache::lonnet::metadata($url,$stores.'.display');
     $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 5605  sub editgrades { Line 4014  sub editgrades {
  '<th align="center">'.&mt('New').' '.$display.'</th>';   '<th align="center">'.&mt('New').' '.$display.'</th>';
     $columns{$partid}+=2;      $columns{$partid}+=2;
  }   }
         $totcolspan += $columns{$partid};  
     }      }
     foreach my $partid (@partid) {      foreach my $partid (@partid) {
  my $display_part=&get_display_part($partid,$symb);   my $display_part=&get_display_part($partid,$symb);
Line 5620  sub editgrades { Line 4028  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 $line;
  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];   $line .= '<td>'.&nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';
         my $canmodify = &canmodify($usec);   my $usec=$classlist->{"$uname:$udom"}[5];
         my $line = '<td'.($canmodify?'':' colspan="2"').'>'.   if (!&canmodify($usec)) {
                    &nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';      my $numcols=scalar(@partid)*4+2;
         if (!$canmodify) {      push(@noupdate,
             push(@noupdate,   $line."<td colspan=\"$numcols\"><span class=\"LC_warning\">".
                  $line."<td colspan=\"$totcolspan\"><span class=\"LC_warning\">".   &mt('Not allowed to modify student')."</span></td></tr>");
                  &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 5648  sub editgrades { Line 4054  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 5689  sub editgrades { Line 4094  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 5711  sub editgrades { Line 4111  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 5724  sub editgrades { Line 4127  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 ($queueable{$part}) {      if ( $record{'resource.'.$part.'.awarded'} eq '' ) {
         if ( $record{'resource.'.$part.'.awarded'} eq '' ) {   $all_graded = 0;
     $all_graded = 0;      } else {
         } else {   $none_graded = 0;
     $none_graded = 0;      }
         }  
                     }  
  }   }
   
  if ($all_graded || $none_graded) {   if ($all_graded || $none_graded) {
Line 5763  sub editgrades { Line 4146  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,$usec,\%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 5779  sub editgrades { Line 4157  sub editgrades {
         }          }
     }      }
     if (@noupdate) {      if (@noupdate) {
         my $numcols=$totcolspan+2;  # my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;
    my $numcols=scalar(@partid)*4+2;
  $result .= &Apache::loncommon::start_data_table_row('LC_empty_row').   $result .= &Apache::loncommon::start_data_table_row('LC_empty_row').
     '<td align="center" colspan="'.$numcols.'">'.      '<td align="center" colspan="'.$numcols.'">'.
     &mt('No Changes Occurred For the Students Below').      &mt('No Changes Occurred For the Students Below').
Line 5909  sub csvuploadmap_header { Line 4288  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 5919  sub csvuploadmap_header { Line 4296  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 5938  ENDPICK Line 4314  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 5950  sub csvupload_fields { Line 4322  sub csvupload_fields {
     }      }
   
     my @fields=(['ID','Student/Employee ID'],      my @fields=(['ID','Student/Employee ID'],
                   ['clicker','Clicker ID'],
  ['username','Student Username'],   ['username','Student Username'],
  ['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',$toolsymb);   my $display=&Apache::lonnet::metadata($url,$part.'.display');
  my $name=$part;   my $name=$part;
  if  (!$display) { $display = $name; }   if  (!$display) { $display = $name; }
  @datum=($name,$display);   @datum=($name,$display);
Line 6013  sub upcsvScores_form { Line 4386  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 6026  ENDUPFORM Line 4398  ENDUPFORM
   
   
 sub csvuploadmap {  sub csvuploadmap {
     my ($request,$symb) = @_;      my ($request,$symb)= @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
     my $datatoken;      my $datatoken;
     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 6076  sub csvuploadmap { Line 4447  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 6091  ENDPICK Line 4460  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 6125  sub get_fields { Line 4494  sub get_fields {
 }  }
   
 sub csvuploadassign {  sub csvuploadassign {
     my ($request,$symb) = @_;      my ($request,$symb)= @_;
     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 6177  sub csvuploadassign { Line 4523  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 6205  sub csvuploadassign { Line 4583  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 6232  sub csvuploadassign { Line 4605  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}};
     }      }
  }   }
  if (! %grades) {   if (! %grades) { 
            push(@skipped,&mt("[_1]: no data to save","$username:$domain"));              push(@skipped,&mt("[_1]: no data to save","$username:$domain")); 
         } else {          } else {
    $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";     $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
Line 6264  sub csvuploadassign { Line 4621  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,$cdom,$cnum,                &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,
                                                      $domain,$username);                                               $env{'course.'.$env{'request.course.id'}.'.domain'},
                                                $env{'course.'.$env{'request.course.id'}.'.num'},
                                                $domain,$username);
               $countdone++;                $countdone++;
               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,$usec,\%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>");
Line 6345  LISTJAVASCRIPT Line 4681  LISTJAVASCRIPT
     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'};
     my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};  
   
     my $result='<h3><span class="LC_info">&nbsp;'.      my $result='<h3><span class="LC_info">&nbsp;'.
  &mt('Manual Grading by Page or Sequence').'</span></h3>';   &mt('Manual Grading by Page or Sequence').'</span></h3>';
Line 6364  LISTJAVASCRIPT Line 4699  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 6413  LISTJAVASCRIPT Line 4748  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 6435  LISTJAVASCRIPT Line 4770  LISTJAVASCRIPT
  '<th>'.&nameUserString('header').'</th>'.   '<th>'.&nameUserString('header').'</th>'.
  &Apache::loncommon::end_data_table_header_row();   &Apache::loncommon::end_data_table_header_row();
     
     my (undef,undef,$fullname) = &getclasslist($getsec,'1',$getgroup);      my (undef,undef,$fullname) = &getclasslist($getsec,'1');
     my $ptr = 1;      my $ptr = 1;
     foreach my $student (sort       foreach my $student (sort 
  {   {
Line 6485  sub getSymbMap { Line 4820  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_gradable(); }, 0) ) {   if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 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 6516  sub displayPage { Line 4851  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 6582  sub displayPage { Line 4917  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_gradable()) {          if (ref($curRes) && $curRes->is_problem()) {
     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 6597  sub displayPage { Line 4931  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 ($is_tool) {      if ($env{'form.vProb'} eq 'yes' ) {
                 $studentTable.='&nbsp;<b>'.$title.'</b><br />';   $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
             } else {       undef,'both',\%form);
         if ($env{'form.vProb'} eq 'yes' ) {      } else {
     $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,   my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);
            undef,'both',\%form);   $companswer =~ s|<form(.*?)>||g;
         } else {   $companswer =~ s|</form>||g;
     my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);  # while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
     $companswer =~ s|<form(.*?)>||g;  #    $companswer =~ s/$1/ /ms;
     $companswer =~ s|</form>||g;  #    $request->print('match='.$1."<br />\n");
 #    while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>  # }
 #        $companswer =~ s/$1/ /ms;  # $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
 #        $request->print('match='.$1."<br />\n");   $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>'.&mt('Correct answer').':</b><br />'.$companswer;
 #    }  
 #    $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 '') {
                     my $msg = &mt('No recorded submission for this problem.');      $studentTable.='<br />&nbsp;<span class="LC_warning">'.&mt('No recorded submission for this problem.').'</span><br />';
                     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 6641  sub displayPage { Line 4967  sub displayPage {
  }   }
     } 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 6663  sub displayPage { Line 4989  sub displayPage {
  }   }
         $curRes = $iterator->next();          $curRes = $iterator->next();
     }      }
     my $disabled;  
     unless (&canmodify($usec)) {  
         $disabled = ' disabled="disabled"';  
     }  
   
     $studentTable.=      $studentTable.=
         '</table>'."\n".          '</table>'."\n".
         '<input type="button" value="'.&mt('Save').'"'.$disabled.' '.          '<input type="button" value="'.&mt('Save').'" '.
         'onclick="javascript:checkSubmitPage(this.form,'.$question.');" />'.          'onclick="javascript:checkSubmitPage(this.form,'.$question.');" />'.
         '</form>'."\n";          '</form>'."\n";
     $request->print($studentTable);      $request->print($studentTable);
Line 6682  sub displaySubByDates { Line 5004  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>'.($is_tool?&mt('Grade'):&mt('Submission')).'</th>'.   '<th>'.&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 6697  sub displaySubByDates { Line 5018  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'})) {
         if ($is_tool) {   return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';
             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 6734  sub displaySubByDates { Line 5051  sub displaySubByDates {
             if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {              if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {
                 $hidden = 1;                  $hidden = 1;
             }              }
     my @matchKey;      my @matchKey = ($isTask ? sort(grep /^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys)
             if ($isTask) {              : sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));
                 @matchKey = sort(grep(/^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys));      
             } elsif ($is_tool) {  
                 @matchKey = sort(grep(/^resource\.\Q$partid\E\.awarded$/,@versionKeys));  
             } else {  
  @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) {                      
                         $displaySub[0].=$$record{"$version:resource.$partid.awarded"};      my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
                  : ($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 ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)                          my ($trial,$rndseed,$newvariation);
                        : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));                          if ($type eq 'randomizetry') {
                         $displaySub[0].='<span class="LC_nobreak">';                              $trial = $$record{"$where.$partid.tries"};
                         $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'                              $rndseed = $$record{"$where.$partid.rndseed"};
                                        .' <span class="LC_internal_info">'  
                                        .'('.&mt('Response ID: [_1]',$responseId).')'  
                                        .'</span>'  
                                        .' <b>';  
                         if ($hidden) {  
                             $displaySub[0].= &mt('Anonymous Survey').'</b>';  
                         } else {  
                             my ($trial,$rndseed,$newvariation);  
                             if ($type eq 'randomizetry') {  
                                 $trial = $$record{"$where.$partid.tries"};  
                                 $rndseed = $$record{"$where.$partid.rndseed"};  
                             }  
             if ($$record{"$where.$partid.tries"} eq '') {  
         $displaySub[0].=&mt('Trial not counted');  
             } else {  
         $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});  
            if (!exists($orders{$partid})) { $orders{$partid}={}; }  
            if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {  
        $orders{$partid}->{$responseId}=  
             &get_order($partid,$responseId,$symb,$uname,$udom,  
                                                $no_increment,$type,$trial,$rndseed);  
             }  
             $displaySub[0].='</b>'.$newvariation.'</span>'; # /nobreak  
             $displaySub[0].='&nbsp; '.  
         &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).'<br />';  
                         }                          }
           if ($$record{"$where.$partid.tries"} eq '') {
       $displaySub[0].=&mt('Trial not counted');
           } else {
       $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});
           if (!exists($orders{$partid})) { $orders{$partid}={}; }
           if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {
       $orders{$partid}->{$responseId}=
           &get_order($partid,$responseId,$symb,$uname,$udom,
                                              $no_increment,$type,$trial,$rndseed);
           }
           $displaySub[0].='</b>'.$newvariation.'</span>'; # /nobreak
           $displaySub[0].='&nbsp; '.
       &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).'<br />';
                     }                      }
  }   }
     }      }
Line 6806  sub displaySubByDates { Line 5115  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"}.
                 unless ($is_tool) {      ' (<b>'.&mt('Part').':</b> '.$display_part.')';
     $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"}.
                 unless ($is_tool) {      ' (<b>'.&mt('Part').':</b> '.$display_part.')';
     $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 6886  sub updateGradeByPage { Line 5187  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 6895  sub updateGradeByPage { Line 5195  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 6908  sub updateGradeByPage { Line 5207  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 6950  sub updateGradeByPage { Line 5230  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 6978  sub updateGradeByPage { Line 5257  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 7008  sub updateGradeByPage { Line 5282  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,\%queueable);       $cdom,$cnum,$udom,$uname);
     }      }
           
             if ($aggregateflag) {              if ($aggregateflag) {
Line 7022  sub updateGradeByPage { Line 5296  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 7038  sub updateGradeByPage { Line 5309  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,%launch_to_symb);  
         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;  
                             $launch_to_symb{$launcher} = $possible;  
                         }  
                         $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;  
                                 $launch_to_symb{$launcher} = $possible;  
                             }  
                         }  
                     } elsif (grep(/^\Q$possible$\E$/,@updates)) {  
                         foreach my $launcher (keys(%{$passback{$possible}})) {  
                             $possrespb{$launcher} = 1;  
                             $launch_to_symb{$launcher} = $possible;  
                         }  
                         $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') {  
                     if ($launch_to_symb{$key}) {  
                         $needpb{$key} = $launch_to_symb{$key};  
                     }  
                 }  
             }  
             my @symbs = keys(%uniqsymbs);  
             &process_passbacks('updatebypage',\@symbs,$cdom,$cnum,$udom,$uname,$usec,\%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 7196  the homework problem. Line 5381  the homework problem.
   
 sub defaultFormData {  sub defaultFormData {
     my ($symb)=@_;      my ($symb)=@_;
     return '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />';      return '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />';
 }  }
   
   
Line 7349  sub scantron_uploads { Line 5534  sub scantron_uploads {
 sub scantron_scantab {  sub scantron_scantab {
     my $result='<select name="scantron_format">'."\n";      my $result='<select name="scantron_format">'."\n";
     $result.='<option></option>'."\n";      $result.='<option></option>'."\n";
     my @lines = &Apache::lonnet::get_scantronformat_file();      my @lines = &get_scantronformat_file();
     if (@lines > 0) {      if (@lines > 0) {
         foreach my $line (@lines) {          foreach my $line (@lines) {
             next if (($line =~ /^\#/) || ($line eq ''));              next if (($line =~ /^\#/) || ($line eq ''));
Line 7361  sub scantron_scantab { Line 5546  sub scantron_scantab {
     return $result;      return $result;
 }  }
   
   =pod
   
   =item get_scantronformat_file
   
     Returns an array containing lines from the scantron format file for
     the domain of the course.
   
     If a url for a custom.tab file is listed in domain's configuration.db, 
     lines are from this file.
   
     Otherwise, if a default.tab has been published in RES space by the 
     domainconfig user, lines are from this file.
   
     Otherwise, fall back to getting lines from the legacy file on the
     local server:  /home/httpd/lonTabs/default_scantronformat.tab    
   
   =cut
   
   sub get_scantronformat_file {
       my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
       my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$cdom);
       my $gottab = 0;
       my @lines;
       if (ref($domconfig{'scantron'}) eq 'HASH') {
           if ($domconfig{'scantron'}{'scantronformat'} ne '') {
               my $formatfile = &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.$domconfig{'scantron'}{'scantronformat'});
               if ($formatfile ne '-1') {
                   @lines = split("\n",$formatfile,-1);
                   $gottab = 1;
               }
           }
       }
       if (!$gottab) {
           my $confname = $cdom.'-domainconfig';
           my $default = $Apache::lonnet::perlvar{'lonDocRoot'}.'/res/'.$cdom.'/'.$confname.'/default.tab';
           my $formatfile =  &Apache::lonnet::getfile($default);
           if ($formatfile ne '-1') {
               @lines = split("\n",$formatfile,-1);
               $gottab = 1;
           }
       }
       if (!$gottab) {
           my @domains = &Apache::lonnet::current_machine_domains();
           if (grep(/^\Q$cdom\E$/,@domains)) {
               my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
               @lines = <$fh>;
               close($fh);
           } else {
               my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/default_scantronformat.tab');
               @lines = <$fh>;
               close($fh);
           }
       }
       return @lines;
   }
   
 =pod   =pod 
   
 =item scantron_CODElist  =item scantron_CODElist
Line 7442  sub scantron_selectphase { Line 5683  sub scantron_selectphase {
     if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||      if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||
         &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {          &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'};      '.&Apache::loncommon::start_data_table('LC_scantron_action').'
         my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};         '.&Apache::loncommon::start_data_table_header_row().'
         my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');              <th>
         &js_escape(\$alertmsg);                &nbsp;'.&mt('Specify a bubblesheet data file to upload.').'
         my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($cdom);              </th>
         $r->print(&Apache::lonhtmlcommon::scripttag('         '.&Apache::loncommon::end_data_table_header_row().'
          '.&Apache::loncommon::start_data_table_row().'
               <td>
   ');
       my $default_form_data=&defaultFormData($symb);
       my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
       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.');
       &js_escape(\$alertmsg);
       $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));      }'));
         $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="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').'                  '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'
               '.&Apache::loncommon::start_data_table_header_row().'                  <br />
                 <th>                  <input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
                 &nbsp;'.&mt('Specify a bubblesheet data file to upload.').'                </form>
                 </th>  ');
               '.&Apache::loncommon::end_data_table_header_row().'  
               '.&Apache::loncommon::start_data_table_row().'  
             <td>  
                 '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'<br />'."\n");  
         if ($formatoptions) {  
             $r->print('</td>  
                  '.&Apache::loncommon::end_data_table_row().'  
                  '.&Apache::loncommon::start_data_table_row().'  
                  <td>'.$formattitle.('&nbsp;'x2).$formatoptions.'  
                  </td>  
                  '.&Apache::loncommon::end_data_table_row().'  
                  '.&Apache::loncommon::start_data_table_row().'  
                  <td>'  
             );  
         } else {  
             $r->print(' <br />');  
         }  
         $r->print('<input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />  
               </td>  
              '.&Apache::loncommon::end_data_table_row().'  
              '.&Apache::loncommon::end_data_table().'  
              </form>'  
         );  
   
           $r->print('
               </td>
          '.&Apache::loncommon::end_data_table_row().'
          '.&Apache::loncommon::end_data_table().'
   ');
     }      }
   
     # Chunk of form to prompt for a file to grade and how:      # Chunk of form to prompt for a file to grade and how:
Line 7543  sub scantron_selectphase { Line 5775  sub scantron_selectphase {
         
     $r->print($result);      $r->print($result);
   
   
   
     # Chunk of the form that prompts to view a scoring office file,      # Chunk of the form that prompts to view a scoring office file,
     # corrected file, skipped records in a file.      # corrected file, skipped records in a file.
   
Line 7604  sub scantron_selectphase { Line 5838  sub scantron_selectphase {
     return;      return;
 }  }
   
   =pod
   
   =item get_scantron_config
   
      Parse and return the bubblesheet configuration line selected as a
      hash of configuration file fields.
   
    Arguments:
       which - the name of the configuration to parse from the file.
   
   
    Returns:
               If the named configuration is not in the file, an empty
               hash is returned.
       a hash with the fields
         name         - internal name for the this configuration setup
         description  - text to display to operator that describes this config
         CODElocation - if 0 or the string 'none'
                             - no CODE exists for this config
                        if -1 || the string 'letter'
                             - a CODE exists for this config and is
                               a string of letters
                        Unsupported value (but planned for future support)
                             if a positive integer
                                  - The CODE exists as the first n items from
                                    the question section of the form
                             if the string 'number'
                                  - The CODE exists for this config and is
                                    a string of numbers
         CODEstart   - (only matter if a CODE exists) column in the line where
                        the CODE starts
         CODElength  - length of the CODE
         IDstart     - column where the student/employee ID starts
         IDlength    - length of the student/employee ID info
         Qstart      - column where the information from the bubbled
                       'questions' start
         Qlength     - number of columns comprising a single bubble line from
                       the sheet. (usually either 1 or 10)
         Qon         - either a single character representing the character used
                       to signal a bubble was chosen in the positional setup, or
                       the string 'letter' if the letter of the chosen bubble is
                       in the final, or 'number' if a number representing the
                       chosen bubble is in the file (1->A 0->J)
         Qoff        - the character used to represent that a bubble was
                       left blank
         PaperID     - if the scanning process generates a unique number for each
                       sheet scanned the column that this ID number starts in
         PaperIDlength - number of columns that comprise the unique ID number
                         for the sheet of paper
         FirstName   - column that the first name starts in
         FirstNameLength - number of columns that the first name spans
    
         LastName    - column that the last name starts in
         LastNameLength - number of columns that the last name spans
         BubblesPerRow - number of bubbles available in each row used to 
                         bubble an answer. (If not specified, 10 assumed).
   
   =cut
   
   sub get_scantron_config {
       my ($which) = @_;
       my @lines = &get_scantronformat_file();
       my %config;
       #FIXME probably should move to XML it has already gotten a bit much now
       foreach my $line (@lines) {
    my ($name,$descrip)=split(/:/,$line);
    if ($name ne $which ) { next; }
    chomp($line);
    my @config=split(/:/,$line);
    $config{'name'}=$config[0];
    $config{'description'}=$config[1];
    $config{'CODElocation'}=$config[2];
    $config{'CODEstart'}=$config[3];
    $config{'CODElength'}=$config[4];
    $config{'IDstart'}=$config[5];
    $config{'IDlength'}=$config[6];
    $config{'Qstart'}=$config[7];
     $config{'Qlength'}=$config[8];
    $config{'Qoff'}=$config[9];
    $config{'Qon'}=$config[10];
    $config{'PaperID'}=$config[11];
    $config{'PaperIDlength'}=$config[12];
    $config{'FirstName'}=$config[13];
    $config{'FirstNamelength'}=$config[14];
    $config{'LastName'}=$config[15];
    $config{'LastNamelength'}=$config[16];
           $config{'BubblesPerRow'}=$config[17];
    last;
       }
       return %config;
   }
   
 =pod   =pod 
   
 =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 7649  sub username_to_idmap { Line 5977  sub username_to_idmap {
    Process a requested correction to a scanline.     Process a requested correction to a scanline.
   
   Arguments:    Arguments:
     $scantron_config   - hash from &Apache::lonnet::get_scantron_config()      $scantron_config   - hash from &get_scantron_config()
     $scan_data         - hash of correction information       $scan_data         - hash of correction information 
                           (see &scantron_getfile())                            (see &scantron_getfile())
     $line              - existing scanline      $line              - existing scanline
Line 7809  sub digits_to_letters { Line 6137  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 7839  sub digits_to_letters { Line 6167  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 7932  sub scantron_parse_scanline { Line 6260  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 7946  sub scantron_parse_scanline { Line 6274  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 8006  sub scantron_parse_scanline { Line 6334  sub scantron_parse_scanline {
 }  }
   
 sub get_master_seq {  sub get_master_seq {
     my ($resources,$master_seq,$symb_to_resource,$need_symb_in_map,$symb_for_examcode) = @_;      my ($resources,$master_seq,$symb_to_resource) = @_;
     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 8019  sub get_master_seq { Line 6344  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 8096  sub scantron_validator_lettnum { Line 6413  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 8343  sub scantron_filter { Line 6660  sub scantron_filter {
   
 sub scantron_process_corrections {  sub scantron_process_corrections {
     my ($r) = @_;      my ($r) = @_;
     my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     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 $which=$env{'form.scantron_line'};      my $which=$env{'form.scantron_line'};
Line 8392  sub scantron_process_corrections { Line 6709  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 8512  sub check_for_error { Line 6829  sub check_for_error {
 sub scantron_warning_screen {  sub scantron_warning_screen {
     my ($button_text,$symb)=@_;      my ($button_text,$symb)=@_;
     my $title=&Apache::lonnet::gettitle($env{'form.selectpage'});      my $title=&Apache::lonnet::gettitle($env{'form.selectpage'});
     my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my $CODElist;      my $CODElist;
     if ($scantron_config{'CODElocation'} &&      if ($scantron_config{'CODElocation'} &&
  $scantron_config{'CODEstart'} &&   $scantron_config{'CODEstart'} &&
Line 8668  sub scantron_validate_file { Line 6985  sub scantron_validate_file {
     #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;
     my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);      my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
Line 8695  sub scantron_validate_file { Line 7012  sub scantron_validate_file {
     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';
Line 8725  sub scantron_validate_file { Line 7042  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 9104  sub scantron_validate_sequence { Line 7421  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 9127  sub scantron_validate_ID { Line 7445  sub scantron_validate_ID {
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
     my $nav_error;      my $nav_error;
Line 9228  sub scantron_get_correction { Line 7546  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 9251  sub scantron_get_correction { Line 7569  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 9349  ENDSCRIPT Line 7667  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 9374  sub verify_bubbles_checked { Line 7692  sub verify_bubbles_checked {
     my $ansnumstr = join('","',@ansnums);      my $ansnumstr = join('","',@ansnums);
     my $warning = &mt("A bubble or 'No bubble' selection has not been made for one or more lines.");      my $warning = &mt("A bubble or 'No bubble' selection has not been made for one or more lines.");
     &js_escape(\$warning);      &js_escape(\$warning);
     my $output = &Apache::lonhtmlcommon::scripttag(<<ENDSCRIPT);      my $output = &Apache::lonhtmlcommon::scripttag((<<ENDSCRIPT));
 function verify_bubble_radio(form) {  function verify_bubble_radio(form) {
     var ansnumArray = new Array ("$ansnumstr");      var ansnumArray = new Array ("$ansnumstr");
     var need_bubble_count = 0;      var need_bubble_count = 0;
Line 9459  sub questions_to_line_list { Line 7777  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 9490  for multi and missing bubble cases). Line 7808  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 9542  sub prompt_for_corrections { Line 7861  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 9560  sub prompt_for_corrections { Line 7879  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 9587  sub prompt_for_corrections { Line 7915  sub prompt_for_corrections {
   
  Arguments:   Arguments:
     $r           - Apache request object      $r           - Apache request object
     $scan_config - hash from &Apache::lonnet::get_scantron_config()      $scan_config - hash from &get_scantron_config()
     $line        - Number of the line being displayed.      $line        - Number of the line being displayed.
     $questionnum - Question number (may include subquestion)      $questionnum - Question number (may include subquestion)
     $error       - Type of error.      $error       - Type of error.
Line 9600  sub scantron_bubble_selector { Line 7928  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 9751  sub get_codes { Line 8079  sub get_codes {
   
 sub scantron_validate_CODE {  sub scantron_validate_CODE {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
     my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     if ($scantron_config{'CODElocation'} &&      if ($scantron_config{'CODElocation'} &&
  $scantron_config{'CODEstart'} &&   $scantron_config{'CODEstart'} &&
  $scantron_config{'CODElength'}) {   $scantron_config{'CODElength'}) {
Line 9825  sub scantron_validate_doublebubble { Line 8153  sub scantron_validate_doublebubble {
         &Apache::lonnet::decode_symb($env{'form.selectpage'});          &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
Line 9843  sub scantron_validate_doublebubble { Line 8171  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 9930  sub scantron_get_maxbubble { Line 8247  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 9981  sub scantron_get_maxbubble { Line 8298  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 10018  sub scantron_validate_missingbubbles { Line 8335  sub scantron_validate_missingbubbles {
         &Apache::lonnet::decode_symb($env{'form.selectpage'});          &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
Line 10037  sub scantron_validate_missingbubbles { Line 8354  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 10073  sub scantron_validate_missingbubbles { Line 8379  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 10086  sub scantron_validate_missingbubbles { Line 8392  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 10158  sub hand_bubble_option { Line 8464  sub hand_bubble_option {
         }          }
     }      }
     if ($needs_hand_bubbles) {      if ($needs_hand_bubbles) {
         my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});          my %scantron_config=&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);
         return &mt('The sequence to be graded contains response types which are handgraded.').'<p>'.          return &mt('The sequence to be graded contains response types which are handgraded.').'<p>'.
                &mt('If you have already graded these by bubbling sheets to indicate points awarded, [_1]what point value is assigned to a filled last bubble in each row?','<br />').                 &mt('If you have already graded these by bubbling sheets to indicate points awarded, [_1]what point value is assigned to a filled last bubble in each row?','<br />').
Line 10177  sub scantron_process_students { Line 8483  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=&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 10189  sub scantron_process_students { Line 8495  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,%symb_for_examcode);          %grader_randomlists_by_symb);
     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 10211  sub scantron_process_students { Line 8506  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,1,\%symb_for_examcode);          $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
         if ($nav_error) {          if ($nav_error) {
             $r->print(&navmap_errormsg());              $r->print(&navmap_errormsg());
             return '';              return '';
Line 10256  SCANTRONFORM Line 8551  SCANTRONFORM
  return ''; # Dunno why the other returns return '' rather than just returning.   return ''; # Dunno why the other returns return '' rather than just returning.
     }      }
   
     my %lettdig = &Apache::lonnet::letter_to_digits();      my %lettdig = &letter_to_digits();
     my $numletts = scalar(keys(%lettdig));      my $numletts = scalar(keys(%lettdig));
     my %orderedforcode;      my %orderedforcode;
   
Line 10273  SCANTRONFORM Line 8568  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 10303  SCANTRONFORM Line 8598  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 10358  SCANTRONFORM Line 8653  SCANTRONFORM
         }          }
   
         if (($scancode) && ($randomorder || $randompick)) {          if (($scancode) && ($randomorder || $randompick)) {
             foreach my $key (keys(%symb_for_examcode)) {              my $parmresult =
                 my $symb_in_map = $symb_for_examcode{$key};                  &Apache::lonparmset::storeparm_by_symb($symb,
                 if ($symb_in_map ne '') {                                                         '0_examcode',2,$scancode,
                     my $parmresult =                                                         'string_examcode',$uname,
                         &Apache::lonparmset::storeparm_by_symb($symb_in_map,                                                         $udom);
                                                                '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 10397  SCANTRONFORM Line 8687  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 10490  sub graders_resources_pass { Line 8780  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 10517  sub users_order  { Line 8807  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 10530  sub users_order  { Line 8820  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 10586  sub grade_student_bubbles { Line 8876  sub grade_student_bubbles {
 }  }
   
 sub scantron_upload_scantron_data {  sub scantron_upload_scantron_data {
     my ($r,$symb) = @_;      my ($r,$symb)=@_;
     my $dom = $env{'request.role.domain'};      my $dom = $env{'request.role.domain'};
     my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($dom);  
     my $domdesc = &Apache::lonnet::domain($dom,'description');      my $domdesc = &Apache::lonnet::domain($dom,'description');
     $r->print(&Apache::loncommon::coursebrowser_javascript($dom));      $r->print(&Apache::loncommon::coursebrowser_javascript($dom));
     my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',      my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',
   '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 10628  sub scantron_upload_scantron_data { Line 8917  sub scantron_upload_scantron_data {
         return;          return;
     }      }
   
     '.$formatjs.'  
 '));  '));
     $r->print('      $r->print('
 <h3>'.&mt('Send bubblesheet data to a course').'</h3>  <h3>'.&mt('Send bubblesheet data to a course').'</h3>
Line 10644  sub scantron_upload_scantron_data { Line 8932  sub scantron_upload_scantron_data {
   &Apache::lonhtmlcommon::row_closure().    &Apache::lonhtmlcommon::row_closure().
   &Apache::lonhtmlcommon::row_title(&mt('Domain')).    &Apache::lonhtmlcommon::row_title(&mt('Domain')).
   '<input name="domainid" type="hidden" />'.$domdesc.    '<input name="domainid" type="hidden" />'.$domdesc.
   &Apache::lonhtmlcommon::row_closure());    &Apache::lonhtmlcommon::row_closure().
     if ($formatoptions) {  
         $r->print(&Apache::lonhtmlcommon::row_title($formattitle).$formatoptions.  
                   &Apache::lonhtmlcommon::row_closure());  
     }  
     $r->print(  
   &Apache::lonhtmlcommon::row_title(&mt('File to upload')).    &Apache::lonhtmlcommon::row_title(&mt('File to upload')).
   '<input type="file" name="upfile" size="50" />'.    '<input type="file" name="upfile" size="50" />'.
   &Apache::lonhtmlcommon::row_closure(1).    &Apache::lonhtmlcommon::row_closure(1).
Line 10662  sub scantron_upload_scantron_data { Line 8945  sub scantron_upload_scantron_data {
     return '';      return '';
 }  }
   
 sub scantron_upload_dataformat {  
     my ($dom) = @_;  
     my ($formatoptions,$formattitle,$formatjs);  
     $formatjs = <<'END';  
 function toggleScantab(form) {  
    return;  
 }  
 END  
     my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$dom);  
     if (ref($domconfig{'scantron'}) eq 'HASH') {  
         if (ref($domconfig{'scantron'}{'config'}) eq 'HASH') {  
             if (keys(%{$domconfig{'scantron'}{'config'}}) > 1) {  
                 if (($domconfig{'scantron'}{'config'}{'dat'}) &&  
                     (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH')) {  
                     if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {  
                         if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {  
                             my ($onclick,$formatextra,$singleline);  
                             my @lines = &Apache::lonnet::get_scantronformat_file();  
                             my $count = 0;  
                             foreach my $line (@lines) {  
                                 next if (($line =~ /^\#/) || ($line eq ''));  
                                 $singleline = $line;  
                                 $count ++;  
                             }  
                             if ($count > 1) {  
                                 $formatextra = '<div style="display:none" id="bubbletype">'.  
                                                '<span class="LC_nobreak">'.  
                                                &mt('Bubblesheet type').':&nbsp;'.  
                                                &scantron_scantab().'</span></div>';  
                                 $onclick = ' onclick="toggleScantab(this.form);"';  
                                 $formatjs = <<"END";  
 function toggleScantab(form) {  
     var divid = 'bubbletype';  
     if (document.getElementById(divid)) {  
         var radioname = 'fileformat';  
         var num = form.elements[radioname].length;  
         if (num) {  
             for (var i=0; i<num; i++) {  
                 if (form.elements[radioname][i].checked) {  
                     var chosen = form.elements[radioname][i].value;  
                     if (chosen == 'dat') {  
                         document.getElementById(divid).style.display = 'none';  
                     } else if (chosen == 'csv') {  
                         document.getElementById(divid).style.display = 'block';  
                     }  
                 }  
             }  
         }  
     }  
     return;  
 }  
   
 END  
                             } elsif ($count == 1) {  
                                 my $formatname = (split(/:/,$singleline,2))[0];  
                                 $formatextra = '<input type="hidden" name="scantron_format" value="'.$formatname.'" />';  
                             }  
                             $formattitle = &mt('File format');  
                             $formatoptions = '<label><input name="fileformat" type="radio" value="dat" checked="checked"'.$onclick.' />'.  
                                              &mt('Plain Text (no delimiters)').  
                                              '</label>'.('&nbsp;'x2).  
                                              '<label><input name="fileformat" type="radio" value="csv"'.$onclick.' />'.  
                                              &mt('Comma separated values').'</label>'.$formatextra;  
                         }  
                     }  
                 }  
             } elsif (keys(%{$domconfig{'scantron'}{'config'}}) == 1) {  
                 if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {  
                     if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {  
                         $formattitle = &mt('Bubblesheet type');  
                         $formatoptions = &scantron_scantab();  
                     }  
                 }  
             }  
         }  
     }  
     return ($formatoptions,$formattitle,$formatjs);  
 }  
   
 sub scantron_upload_scantron_data_save {  sub scantron_upload_scantron_data_save {
     my ($r,$symb) = @_;      my($r,$symb)=@_;
     my $doanotherupload=      my $doanotherupload=
  '<br /><form action="/adm/grades" method="post">'."\n".   '<br /><form action="/adm/grades" method="post">'."\n".
  '<input type="hidden" name="command" value="scantronupload" />'."\n".   '<input type="hidden" name="command" value="scantronupload" />'."\n".
Line 10752  sub scantron_upload_scantron_data_save { Line 8957  sub scantron_upload_scantron_data_save {
  !&Apache::lonnet::allowed('usc',   !&Apache::lonnet::allowed('usc',
     $env{'form.domainid'}.'_'.$env{'form.courseid'})) {      $env{'form.domainid'}.'_'.$env{'form.courseid'})) {
  $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(
                 &mt('The file: [_1] you attempted to upload contained no information. Please check that you entered the correct filename.',                  &mt('The file: [_1] you attempted to upload contained no information. Please check that you entered the correct filename.',
                         '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'),1));                          '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'),1));
     } else {      } else {
         my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$env{'form.domainid'});          my $result = 
         my $parser;              &Apache::lonnet::userfileupload('upfile','','scantron','','','',
         if (ref($domconfig{'scantron'}) eq 'HASH') {  
             if (ref($domconfig{'scantron'}{'config'}) eq 'HASH') {  
                 my $is_csv;  
                 my @possibles = keys(%{$domconfig{'scantron'}{'config'}});  
                 if (@possibles > 1) {  
                     if ($env{'form.fileformat'} eq 'csv') {  
                         if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') {  
                             if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {  
                                 if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}}) > 1) {  
                                     $is_csv = 1;  
                                 }  
                             }  
                         }  
                     }  
                 } elsif (@possibles == 1) {  
                     if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') {  
                         if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {  
                             if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}}) > 1) {  
                                 $is_csv = 1;  
                             }  
                         }  
                     }  
                 }  
                 if ($is_csv) {  
                    $parser = $domconfig{'scantron'}{'config'}{'csv'};  
                 }  
             }  
         }  
         my $result =  
             &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]',
Line 10808  sub scantron_upload_scantron_data_save { Line 8983  sub scantron_upload_scantron_data_save {
             ($uploadedfile) = ($result =~ m{/([^/]+)$});              ($uploadedfile) = ($result =~ m{/([^/]+)$});
             $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'},$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 10842  sub validate_uploaded_scantron_file { Line 9017  sub validate_uploaded_scantron_file {
             $idmap{$lckey} = $idmap{$key};              $idmap{$lckey} = $idmap{$key};
         }          }
         my %unique_formats;          my %unique_formats;
         my @formatlines = &Apache::lonnet::get_scantronformat_file();          my @formatlines = &get_scantronformat_file();
         foreach my $line (@formatlines) {          foreach my $line (@formatlines) {
             next if (($line =~ /^\#/) || ($line eq ''));              chomp($line);
             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 10943  sub valid_file { Line 9118  sub valid_file {
 }  }
   
 sub scantron_download_scantron_data {  sub scantron_download_scantron_data {
     my ($r,$symb) = @_;      my ($r,$symb)=@_;
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};      my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
Line 10983  sub checkscantron_results { Line 9158  sub checkscantron_results {
     my ($r,$symb) = @_;      my ($r,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $cid = $env{'request.course.id'};      my $cid = $env{'request.course.id'};
     my %lettdig = &Apache::lonnet::letter_to_digits();      my %lettdig = &letter_to_digits();
     my $numletts = scalar(keys(%lettdig));      my $numletts = scalar(keys(%lettdig));
     my $cnum = $env{'course.'.$cid.'.num'};      my $cnum = $env{'course.'.$cid.'.num'};
     my $cdom = $env{'course.'.$cid.'.domain'};      my $cdom = $env{'course.'.$cid.'.domain'};
     my (undef, undef, $sequence) = &Apache::lonnet::decode_symb($env{'form.selectpage'});      my (undef, undef, $sequence) = &Apache::lonnet::decode_symb($env{'form.selectpage'});
     my %record;      my %record;
     my %scantron_config =      my %scantron_config =
         &Apache::lonnet::get_scantron_config($env{'form.scantron_format'});          &Apache::grades::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)=&Apache::grades::scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
Line 11003  sub checkscantron_results { Line 9178  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 11043  sub checkscantron_results { Line 9207  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 11054  sub checkscantron_results { Line 9217  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 11167  sub checkscantron_results { Line 9329  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 11325  sub verify_scantron_grading { Line 9489  sub verify_scantron_grading {
     return ($counter,$record);      return ($counter,$record);
 }  }
   
   sub letter_to_digits {
       my %lettdig = (
                       A => 1,
                       B => 2,
                       C => 3,
                       D => 4,
                       E => 5,
                       F => 6,
                       G => 7,
                       H => 8,
                       I => 9,
                       J => 0,
                     );
       return %lettdig;
   }
   
   
 #-------- end of section for handling grading scantron forms -------  #-------- end of section for handling grading scantron forms -------
 #  #
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
Line 11335  sub verify_scantron_grading { Line 9516  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='.      return '/adm/grades?symb='.&HTML::Entities::encode(&Apache::lonenc::check_encrypt($symb),'<>&"').'&amp;command='.$cmd;
            &HTML::Entities::encode($cmd,'<>&"');   
 }  }
   
 sub grading_menu {  sub grading_menu {
Line 11345  sub grading_menu { Line 9525  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 11359  sub grading_menu { Line 9539  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 11371  sub grading_menu { Line 9551  sub grading_menu {
   
     $fields{'command'} = 'initialverifyreceipt';      $fields{'command'} = 'initialverifyreceipt';
     my $url5 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);      my $url5 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
       
     my %permissions;  
     if ($perm{'mgr'}) {  
         $permissions{'either'} = 'F';  
         $permissions{'mgr'} = 'F';  
     }  
     if ($perm{'vgr'}) {  
         $permissions{'either'} = 'F';  
         $permissions{'vgr'} = 'F';  
     }  
   
     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 => 'F',
                                 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 => 'F',
                                 icon => 'ungrade_sub.png',                                  icon => 'ungrade_sub.png',
                                 linktitle => 'Grade all submissions that have not been graded yet.'                                  linktitle => 'Grade all submissions that have not been graded yet.'
                         },                          },
   
                         {       linktext => 'Grading table',                          {       linktext => 'Grading table',
                                 url => $url1c,                                  url => $url1c,
                                 permission => $permissions{'either'},                                  permission => 'F',
                                 icon => 'grading_table.png',                                  icon => 'grading_table.png',
                                 linktitle => 'Grade current resource for all students.'                                  linktitle => 'Grade current resource for all students.'
                         },                          },
                         {       linktext => 'Grade page/folder for one student',                          {       linktext => 'Grade page/folder for one student',
                                 url => $url1d,                                  url => $url1d,
                                 permission => $permissions{'either'},                                  permission => 'F',
                                 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 => 'F',
                                 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 =>[
   
                    { linktext => 'Upload Scores',                     { linktext => 'Upload Scores',
                     url => $url2,                      url => $url2,
                     permission => $permissions{'mgr'},                      permission => 'F',
                     icon => 'uploadscores.png',                      icon => 'uploadscores.png',
                     linktitle => 'Specify a file containing the class scores for current resource.'                      linktitle => 'Specify a file containing the class scores for current resource.'
                    },                     },
                    { linktext => 'Process Clicker',                     { linktext => 'Process Clicker',
                     url => $url3,                      url => $url3,
                     permission => $permissions{'mgr'},                      permission => 'F',
                     icon => 'addClickerInfoFile.png',                      icon => 'addClickerInfoFile.png',
                     linktitle => 'Specify a file containing the clicker information for this resource.'                      linktitle => 'Specify a file containing the clicker information for this resource.'
                    },                     },
                    { linktext => 'Grade/Manage/Review Bubblesheets',                     { linktext => 'Grade/Manage/Review Bubblesheets',
                     url => $url4,                      url => $url4,
                     permission => $permissions{'mgr'},                      permission => 'F',
                     icon => 'bubblesheet.png',                      icon => 'bubblesheet.png',
                     linktitle => 'Grade bubblesheet exams, upload/download bubblesheet data files, and review previously graded bubblesheet exams.'                      linktitle => 'Grade bubblesheet exams, upload/download bubblesheet data files, and review previously graded bubblesheet exams.'
                    },                     },
                             {   linktext => 'Verify Receipt Number',                              {   linktext => 'Verify Receipt Number',
                                 url => $url5,                                  url => $url5,
                                 permission => $permissions{'either'},                                  permission => 'F',
                                 icon => 'receipt_number.png',                                  icon => 'receipt_number.png',
                                 linktitle => 'Verify a system-generated receipt number for correct problem solution.'                                  linktitle => 'Verify a system-generated receipt number for correct problem solution.'
                             }                              }
   
                     ]                      ]
             });              });
     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 11469  sub grading_menu { Line 9626  sub grading_menu {
     return $Str;          return $Str;    
 }  }
   
   
 sub ungraded {  sub ungraded {
     my ($request)=@_;      my ($request)=@_;
     &submit_options($request);      &submit_options($request);
Line 11496  sub submit_options_table { Line 9654  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,$is_tool).      $result.=&selectfield(1).
             '<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 11516  sub submit_options_download { Line 9673  sub submit_options_download {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
     my $res_error;  
     my ($partlist,$handgrade,$responseType,$numresp,$numessay,$numdropbox) =  
         &response_type($symb,\$res_error);  
     if ($res_error) {  
         $request->print(&mt('An error occurred retrieving response types'));  
         return;  
     }  
     unless ($numessay) {  
         $request->print(&mt('No essayresponse items found'));  
         return;  
     }  
     my $table;  
     if (ref($partlist) eq 'ARRAY') {  
         if (scalar(@$partlist) > 1 ) {  
             $table = &showResourceInfo($symb,$partlist,$responseType,'gradingMenu',1,1);  
         }  
     }  
   
     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".          '<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 Which to Download Submissions').'
 </h2>'.&selectfield(1,$is_tool).'  </h2>'.&selectfield(1).'
                 <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 11559  sub submit_options { Line 9696  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,$is_tool).'      $result.=&selectfield(1).'
                 <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>';
     return $result;      return $result;
 }  }
   
 sub selectfield {  sub selectfield {
    my ($full,$is_tool)=@_;     my ($full)=@_;
    my %options;     my %options = 
    if ($is_tool) {            (&substatus_options,
        %options =             'select_form_order' => ['yes','queued','graded','incorrect','all']);
            (&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  
   # for a different course from the @Sections global in lonstatistics.pm,  
   # populated by an earlier request.  
   #  
    &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 11617  sub selectfield { Line 9740  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');         $result.='
         if ($is_tool) {  
             $heading = &mt('Transaction Status');  
         }  
         $result.='  
     <fieldset>      <fieldset>
       <legend>        <legend>
         '.$heading.'          '.&mt('Submission Status').'
       </legend>'.        </legend>'.
        &Apache::loncommon::select_form('all','submitonly',\%options).         &Apache::loncommon::select_form('all','submitonly',\%options).
    '</fieldset>';     '</fieldset>';
Line 11643  sub substatus_options { Line 9762  sub substatus_options {
                                       );                                        );
 }  }
   
 sub transtatus_options {  
     return &Apache::lonlocal::texthash(  
                                        'yes'       => 'with score transactions',  
                                        'incorrect' => 'with less than full credit',  
                                        'all'       => 'with any status',  
                                       );  
 }  
   
 sub reset_perm {  sub reset_perm {
     undef(%perm);      undef(%perm);
 }  }
Line 11784  sub process_clicker { Line 9895  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 11845  ENDUPFORM Line 9956  ENDUPFORM
 <input type="text" name="givenanswer" size="50" />  <input type="text" name="givenanswer" size="50" />
 <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />  <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />
 ENDGRADINGFORM  ENDGRADINGFORM
     $result.='</td>'.&Apache::loncommon::end_data_table_row().           $result.='</td>'.&Apache::loncommon::end_data_table_row().
                      &Apache::loncommon::start_data_table_row().'<td>'.(<<ENDPERCFORM);                       &Apache::loncommon::start_data_table_row().'<td>'.(<<ENDPERCFORM);
       <label>$pcorrect: <input type="text" name="pcorrect" size="4" value="$env{'form.pcorrect'}" onchange="sanitycheck()" /></label>        <label>$pcorrect: <input type="text" name="pcorrect" size="4" value="$env{'form.pcorrect'}" onchange="sanitycheck()" /></label>
 <br /><label>$pincorrect: <input type="text" name="pincorrect" size="4" value="$env{'form.pincorrect'}" onchange="sanitycheck()" /></label>  <br /><label>$pincorrect: <input type="text" name="pincorrect" size="4" value="$env{'form.pincorrect'}" onchange="sanitycheck()" /></label>
 <br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />  <br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />
 </form>  </form>'
 ENDPERCFORM  ENDPERCFORM
     $result.='</td>'.      $result.='</td>'.
              &Apache::loncommon::end_data_table_row().               &Apache::loncommon::end_data_table_row().
Line 11859  ENDPERCFORM Line 9970  ENDPERCFORM
 }  }
   
 sub process_clicker_file {  sub process_clicker_file {
     my ($r,$symb) = @_;      my ($r,$symb)=@_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
     my %Saveable_Parameters=&clicker_grading_parameters();      my %Saveable_Parameters=&clicker_grading_parameters();
Line 11931  sub process_clicker_file { Line 10042  sub process_clicker_file {
                         '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'),1);                          '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'),1);
         return $result;          return $result;
     }      }
     my $mimetype;  
     if ($env{'form.upfiletype'} eq 'iclicker') {  
         my $mm = new File::MMagic;  
         $mimetype = $mm->checktype_contents($env{'form.upfile'});  
         unless (($mimetype eq 'text/plain') || ($mimetype eq 'text/html')) {  
             $result.= '<p>'.  
                 &Apache::lonhtmlcommon::confirm_success(  
                     &mt('File format is neither csv (iclicker 6) nor xml (iclicker 7)'),1).'</p>';  
             return $result;  
         }  
     } elsif (($env{'form.upfiletype'} ne 'interwrite') && ($env{'form.upfiletype'} ne 'turning')) {  
         $result .= '<p>'.  
             &Apache::lonhtmlcommon::confirm_success(  
                 &mt('Invalid clicker type: choose one of: i>clicker, Interwrite PRS, or Turning Technologies.'),1).'</p>';  
         return $result;  
     }  
   
 # Were able to get all the info needed, now analyze the file  # Were able to get all the info needed, now analyze the file
   
Line 11973  ENDHEADER Line 10068  ENDHEADER
     my $errormsg='';      my $errormsg='';
     my $number=0;      my $number=0;
     if ($env{'form.upfiletype'} eq 'iclicker') {      if ($env{'form.upfiletype'} eq 'iclicker') {
         if ($mimetype eq 'text/plain') {   ($errormsg,$number)=&iclicker_eval(\@questiontitles,\%responses);
             ($errormsg,$number)=&iclicker_eval(\@questiontitles,\%responses);      }
         } elsif ($mimetype eq 'text/html') {      if ($env{'form.upfiletype'} eq 'interwrite') {
             ($errormsg,$number)=&iclickerxml_eval(\@questiontitles,\%responses);  
         }  
     } elsif ($env{'form.upfiletype'} eq 'interwrite') {  
         ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);          ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);
     } elsif ($env{'form.upfiletype'} eq 'turning') {      }
       if ($env{'form.upfiletype'} eq 'turning') {
         ($errormsg,$number)=&turning_eval(\@questiontitles,\%responses);          ($errormsg,$number)=&turning_eval(\@questiontitles,\%responses);
     }      }
     $result.='<br />'.&mt('Found [_1] question(s)',$number).'<br />'.      $result.='<br />'.&mt('Found [_1] question(s)',$number).'<br />'.
Line 12033  ENDHEADER Line 10126  ENDHEADER
                    "\n".&mt("Username").": <input type='text' name='uname".$id."' />&nbsp;".                     "\n".&mt("Username").": <input type='text' name='uname".$id."' />&nbsp;".
                    "\n".&mt("Domain").": ".                     "\n".&mt("Domain").": ".
                    &Apache::loncommon::select_dom_form($env{'course.'.$env{'request.course.id'}.'.domain'},'udom'.$id).'&nbsp;'.                     &Apache::loncommon::select_dom_form($env{'course.'.$env{'request.course.id'}.'.domain'},'udom'.$id).'&nbsp;'.
                    &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id,'',$id);                     &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id,0,$id);
           $unknown_count++;            $unknown_count++;
        }         }
     }      }
Line 12088  sub iclicker_eval { Line 10181  sub iclicker_eval {
     return ($errormsg,$number);      return ($errormsg,$number);
 }  }
   
 sub iclickerxml_eval {  
     my ($questiontitles,$responses)=@_;  
     my $number=0;  
     my $errormsg='';  
     my @state;  
     my %respbyid;  
     my $p = HTML::Parser->new  
     (  
         xml_mode => 1,  
         start_h =>  
             [sub {  
                  my ($tagname,$attr) = @_;  
                  push(@state,$tagname);  
                  if ("@state" eq "ssn p") {  
                      my $title = $attr->{qn};  
                      $title =~ s/(^\s+|\s+$)//g;  
                      $questiontitles->[$number]=$title;  
                  } elsif ("@state" eq "ssn p v") {  
                      my $id = $attr->{id};  
                      my $entry = $attr->{ans};  
                      $id=~s/^[\#0]+//;  
                      $entry =~s/[^a-zA-Z0-9\.\*\-\+]+//g;  
                      $respbyid{$id}[$number] = $entry;  
                  }  
             }, "tagname, attr"],  
          end_h =>  
                [sub {  
                    my ($tagname) = @_;  
                    if ("@state" eq "ssn p") {  
                        $number++;  
                    }  
                    pop(@state);  
                 }, "tagname"],  
     );  
   
     $p->parse($env{'form.upfile'});  
     $p->eof;  
     foreach my $id (keys(%respbyid)) {  
         $responses->{$id}=join(',',@{$respbyid{$id}});  
     }  
     return ($errormsg,$number);  
 }  
   
 sub interwrite_eval {  sub interwrite_eval {
     my ($questiontitles,$responses)=@_;      my ($questiontitles,$responses)=@_;
     my $number=0;      my $number=0;
Line 12187  sub turning_eval { Line 10237  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 '';}
 # See which part we are saving to  # See which part we are saving to
     my $res_error;      my $res_error;
Line 12196  sub assign_clicker_grades { Line 10247  sub assign_clicker_grades {
     if ($res_error) {      if ($res_error) {
         return &navmap_errormsg();          return &navmap_errormsg();
     }      }
     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 (%skip_passback,%pbsave);  
 # 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().
                  &Apache::loncommon::start_data_table_row().'<td>';               &Apache::loncommon::start_data_table_row().'<td>';
 # Get correct result  # Get correct result
 # FIXME: Possibly need delimiter other than ":"  # FIXME: Possibly need delimiter other than ":"
     my @correct=();      my @correct=();
Line 12262  sub assign_clicker_grades { Line 10309  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;
           for (my $i=0;$i<$number;$i++) {            for (my $i=0;$i<$number;$i++) {
              if  ($correct[$i] eq '-') {               if  ($correct[$i] eq '-') {
                 $realnumber--;                  $realnumber--;
              } elsif (($answer[$i]) || ($answer[$i]=~/^[0\.]+$/)) {               } elsif (($answer[$i]) || ($answer[$i]=~/^[0\.]+$/))  {
                 if ($gradingmechanism eq 'attendance') {                  if ($gradingmechanism eq 'attendance') {
                    $sum+=$pcorrect;                     $sum+=$pcorrect;
                 } elsif ($correct[$i] eq '*') {                  } elsif ($correct[$i] eq '*') {
Line 12309  sub assign_clicker_grades { Line 10356  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++;
              if (keys(%needpb)) {  
                  my (%weights,%awardeds,%excuseds);  
                  my $usec = &Apache::lonnet::getsection($domain,$username,$env{'request.course.id'});  
                  $weights{$symb}{$part} = &Apache::lonnet::EXT("resource.$part.weight",$symb,$domain,$username,$usec);  
                  $awardeds{$symb}{$part} = $ave;  
                  $excuseds{$symb}{$part} = '';  
                  &process_passbacks('clickergrade',[$symb],$cdom,$cnum,$domain,$username,$usec,\%weights,  
                                     \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave);  
              }  
           }            }
        }         }
     }      }
Line 12337  sub navmap_errormsg { Line 10375  sub navmap_errormsg {
 }  }
   
 sub startpage {  sub startpage {
     my ($r,$symb,$crumbs,$onlyfolderflag,$nodisplayflag,$stuvcurrent,$stuvdisp,$nomenu,$head_extra,$onload,$divforres) = @_;      my ($r,$symb,$crumbs,$onlyfolderflag,$nodisplayflag,$stuvcurrent,$stuvdisp,$nomenu,$js) = @_;
     my %args;  
     if ($onload) {  
          my %loaditems = (  
                         'onload' => $onload,  
                       );  
          $args{'add_entries'} = \%loaditems;  
     }  
     if ($nomenu) {      if ($nomenu) {
         $args{'only_body'} = 1;          $r->print(&Apache::loncommon::start_page("Student's Version",$js,{'only_body' => '1'}));
         $r->print(&Apache::loncommon::start_page("Student's Version",$head_extra,\%args));  
     } else {      } else {
         if ($env{'request.course.id'}) {          unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});
             unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});          $r->print(&Apache::loncommon::start_page('Grading',$js,
         }                                                   {'bread_crumbs' => $crumbs}));
         $args{'bread_crumbs'} = $crumbs;          &Apache::lonquickgrades::startGradeScreen($r,($env{'form.symb'}?'probgrading':'grading'));
         $r->print(&Apache::loncommon::start_page('Grading',$head_extra,\%args));  
     }      }
     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));
     }      }
 }  }
   
 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));
     $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 12450  sub handler { Line 10408  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 12473  sub handler { Line 10431  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 12490  sub handler { Line 10444  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);
             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) =
                     &choose_task_version_form($symb,$env{'form.student'},                      &choose_task_version_form($symb,$env{'form.student'},
                                               $env{'form.userdom'});                                                $env{'form.userdom'});
             }              }
             my $divforres;              &startpage($request,$symb,[{href=>"", text=>"Student Submissions"}],undef,undef,$stuvcurrent,$stuvdisp,undef,$js);
             if ($env{'form.student'} eq '') {  
                 $js .= &part_selector_js();  
                 $onload = "toggleParts('gradesub');";  
             } else {  
                 $divforres = 1;  
             }  
             my $head_extra = $js;  
             unless ($env{'form.vProb'} eq 'no') {  
                 my $csslinks = &Apache::loncommon::css_links($symb);  
                 if ($csslinks) {  
                     $head_extra .= "\n$csslinks";  
                 }  
             }  
             &startpage($request,$symb,[{href=>"", text=>"Student Submissions"}],undef,undef,  
                        $stuvcurrent,$stuvdisp,undef,$head_extra,$onload,$divforres);  
             if ($versionform) {              if ($versionform) {
                 if ($divforres) {  
                     $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');  
                 }  
                 $request->print($versionform);                  $request->print($versionform);
             }              }
             ($env{'form.student'} eq '' ? &listStudents($request,$symb,'',$divforres) : &submission($request,0,0,$symb,$divforres,$command));              $request->print('<br clear="all" />');
       ($env{'form.student'} eq '' ? &listStudents($request,$symb) : &submission($request,0,0,$symb));
         } 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'},
                                           $env{'form.userdom'},                                            $env{'form.userdom'},
                                           $env{'form.inhibitmenu'});                                            $env{'form.inhibitmenu'});
             my $head_extra = $js;              &startpage($request,$symb,[{href=>"", text=>"Previous Student Version"}],undef,undef,$stuvcurrent,$stuvdisp,$env{'form.inhibitmenu'},$js);
             unless ($env{'form.vProb'} eq 'no') {  
                 my $csslinks = &Apache::loncommon::css_links($symb);  
                 if ($csslinks) {  
                     $head_extra .= "\n$csslinks";  
                 }  
             }  
             &startpage($request,$symb,[{href=>"", text=>"Previous Student Version"}],undef,undef,  
                        $stuvcurrent,$stuvdisp,$env{'form.inhibitmenu'},$head_extra);  
             if ($versionform) {              if ($versionform) {
                 $request->print($versionform);                  $request->print($versionform);
             }              }
             $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;  
             unless ($env{'form.vProb'} eq 'no') {  
                 $csslinks = &Apache::loncommon::css_links($symb,'map');  
             }  
             &startpage($request,$symb,              &startpage($request,$symb,
                                       [{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);
             &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;  
             unless ($env{'form.vProb'} eq 'no') {  
                 $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'}]);
             &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();              &startpage($request,$symb,[{href=>'',text=>'Grade ungraded submissions'}]);
             my $onload = "toggleParts('gradesub');";  
             &startpage($request,$symb,[{href=>'',text=>'Grade ungraded submissions'}],  
                        undef,undef,undef,undef,undef,$js,$onload);  
             $request->print(&listStudents($request,$symb,'graded'));              $request->print(&listStudents($request,$symb,'graded'));
         } elsif ($command eq 'table' && $perm{'vgr'}) {          } elsif ($command eq 'table' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>"", text=>"Grading table"}]);              &startpage($request,$symb,[{href=>"", text=>"Grading table"}]);
Line 12589  sub handler { Line 10507  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 12621  sub handler { Line 10539  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);');      $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'})||
                   &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {    &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,  
                        undef,undef,undef,undef,'toggleScantab(document.rules);');  
             $request->print(&scantron_upload_scantron_data($request,$symb));  
         } elsif ($command eq 'scantronupload_save' &&  
                  (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||  
                   &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($request,$symb)); 
         } elsif ($command eq 'scantron_download' &&    } elsif ($command eq 'scantronupload_save' &&
                  &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {    (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||
     &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_upload_scantron_data_save($request,$symb));
     } elsif ($command eq 'scantron_download' &&
    &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
               &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
        $request->print(&scantron_download_scantron_data($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));
         } elsif ($command eq 'downloadfilesselect' && $perm{'vgr'}) {          } elsif ($command eq 'downloadfilesselect' && $perm{'vgr'}) {
             my $js = &part_selector_js();              &startpage($request,$symb,[{href=>'', text=>'Select which submissions to download'}]);
             my $onload = "toggleParts('gradingMenu');";  
             &startpage($request,$symb,[{href=>'', text=>'Select which submissions to download'}],  
                        undef,undef,undef,undef,undef,$js,$onload);  
             $request->print(&submit_options_download($request,$symb));              $request->print(&submit_options_download($request,$symb));
          } elsif ($command eq 'downloadfileslink' && $perm{'vgr'}) {           } elsif ($command eq 'downloadfileslink' && $perm{'vgr'}) {
             &startpage($request,$symb,              &startpage($request,$symb,
    [{href=>&href_symb_cmd($symb,'downloadfilesselect'), text=>'Select which submissions to download'},     [{href=>&href_symb_cmd($symb,'downloadfilesselect'), text=>'Select which submissions to download'},
     {href=>'', text=>'Download submitted files'}],      {href=>'', text=>'Download submissions'}]);
                undef,undef,undef,undef,undef,undef,undef,1);  
             $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 eq 'initialpassback') {   } elsif ($command) {
             &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());
       } else {
           &Apache::lonquickgrades::endGradeScreen($request);
       }
     &reset_caches();      &reset_caches();
     return OK;      return OK;
 }  }
Line 12760  described at http://www.lon-capa.org. Line 10637  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 12807  ssi_with_retries() Line 10684  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 12816  ssi_with_retries() Line 10762  ssi_with_retries()
     $r           - Apache request object      $r           - Apache request object
     $i           - number of the current scanline      $i           - number of the current scanline
     $scan_record - hash ref as returned from &scantron_parse_scanline()      $scan_record - hash ref as returned from &scantron_parse_scanline()
     $scan_config - hash ref as returned from &Apache::lonnet::get_scantron_config()      $scan_config - hash ref as returned from &get_scantron_config()
     $line        - full contents of the current scanline      $line        - full contents of the current scanline
     $error       - error condition, valid values are      $error       - error condition, valid values are
                    'incorrectCODE', 'duplicateCODE',                     'incorrectCODE', 'duplicateCODE',
Line 12833  ssi_with_retries() Line 10779  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 (or a sub-folder) has randomorder set     $randomorder - True if exam folder has randomorder set
    $randompick  - True if exam folder (or a sub-folder) has randompick set     $randompick  - True if exam 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 12843  ssi_with_retries() Line 10789  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 12922  ssi_with_retries() Line 10869  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.60.2.9  
changed lines
  Added in v.1.742


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