Diff for /loncom/homework/grades.pm between versions 1.596.2.12.2.60.2.10 and 1.788

version 1.596.2.12.2.60.2.10, 2025/01/18 22:07:25 version 1.788, 2022/02/09 23:21:11
Line 44  use Apache::Constants qw(:common :http); Line 44  use Apache::Constants qw(:common :http);
 use Apache::lonlocal;  use Apache::lonlocal;
 use Apache::lonenc;  use Apache::lonenc;
 use Apache::lonstathelpers;  use Apache::lonstathelpers;
   use Apache::lonquickgrades;
 use Apache::bridgetask();  use Apache::bridgetask();
 use Apache::lontexconvert();  use Apache::lontexconvert();
 use Apache::loncourserespicker;  
 use String::Similarity;  use String::Similarity;
 use HTML::Parser();  use HTML::Parser();
 use File::MMagic;  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 65  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 148  sub nameUserString { Line 147  sub nameUserString {
 }  }
   
 #--- Get the partlist and the response type for a given problem. ---  #--- Get the partlist and the response type for a given problem. ---
 #--- Indicate if a response type is coded handgraded or not. ---  
 #--- Count responseIDs, essayresponse items, and dropbox items ---  #--- Count responseIDs, essayresponse items, and dropbox items ---
 #--- Sets response_error pointer to "1" if navmaps object broken ---  #--- Sets response_error pointer to "1" if navmaps object broken ---
 sub response_type {  sub response_type {
Line 638  COMMONJSFUNCTIONS Line 636  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,$filterbyaccstatus,$getgroup,$symb,$submitonly,$filterbysubmstatus) = @_;
     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 664  sub getclasslist {
     #      #
     my %sections;      my %sections;
     my %fullnames;      my %fullnames;
     my %passback;  
     my ($cdom,$cnum,$partlist);      my ($cdom,$cnum,$partlist);
     if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {      if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {
         $cdom = $env{"course.$env{'request.course.id'}.domain"};          $cdom = $env{"course.$env{'request.course.id'}.domain"};
         $cnum = $env{"course.$env{'request.course.id'}.num"};          $cnum = $env{"course.$env{'request.course.id'}.num"};
         my $res_error;          my $res_error;
         ($partlist) = &response_type($symb,\$res_error);          ($partlist) = &response_type($symb,\$res_error);
     } elsif ($filterbypbid) {  
         $cdom = $env{"course.$env{'request.course.id'}.domain"};  
         $cnum = $env{"course.$env{'request.course.id'}.num"};  
     }      }
     foreach my $student (keys(%$classlist)) {      foreach my $student (keys(%$classlist)) {
         my $end      =           my $end      = 
Line 762  sub getclasslist { Line 756  sub getclasslist {
                 }                  }
             }              }
         }          }
         if ($filterbypbid) {  
             if (ref($possibles) eq 'HASH') {  
                 unless (exists($possibles->{$student})) {  
                     delete($classlist->{$student});  
                     next;  
                 }  
             }  
             my $udom =  
                 $classlist->{$student}->[&Apache::loncoursedata::CL_SDOM()];  
             my $uname =  
                 $classlist->{$student}->[&Apache::loncoursedata::CL_SNAME()];  
             if (($udom ne '') && ($uname ne '')) {  
                 my %pbinfo = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',[$filterbypbid],$udom,$uname);  
                 if (ref($pbinfo{$filterbypbid}) eq 'ARRAY') {  
                     $passback{$student} = $pbinfo{$filterbypbid};  
                 } else {  
                     delete($classlist->{$student});  
                     next;  
                 }  
             }  
         }  
  $section = ($section ne '' ? $section : 'none');   $section = ($section ne '' ? $section : 'none');
  if (&canview($section)) {   if (&canview($section)) {
     if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {      if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {
Line 798  sub getclasslist { Line 771  sub getclasslist {
  }   }
     }      }
     my @sections = sort(keys(%sections));      my @sections = sort(keys(%sections));
     return ($classlist,\@sections,\%fullnames,\%passback);      return ($classlist,\@sections,\%fullnames);
 }  }
   
 sub canmodify {  sub canmodify {
Line 1061  sub verifyreceipt { Line 1034  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 
Line 1963  sub listStudents { Line 1065  sub listStudents {
         }          }
     }      }
   
     $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 2252  LISTJAVASCRIPT Line 1380  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) = @_;
Line 2719  INNERJS Line 1799  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 3038  sub handback_box { Line 2119  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 3125  sub files_exist { Line 2206  sub files_exist {
         my ($uname,$udom,$fullname) = split(/:/,$student);          my ($uname,$udom,$fullname) = split(/:/,$student);
         my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},          my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},
       $udom,$uname);        $udom,$uname);
         my ($string)= &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 3250  sub submission { Line 2331  sub submission {
     }      }
   
     if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }      if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }
     unless ($is_tool) {      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'; }
     }      }
Line 3477  sub submission { Line 2558  sub submission {
     #             (3) All transactions (by date)      #             (3) All transactions (by date)
     #             (4) The whole record (with detailed information for all transactions)      #             (4) The whole record (with detailed information for all transactions)
   
     my ($lastsubonly,$partinfo) =      my ($string,$timestamp)= &get_last_submission(\%record,$is_tool);
         &show_last_submission($uname,$udom,$symb,$essayurl,$responseType,$env{'form.lastSub'},  
                               $is_tool,$fullname,\%record,\%coursedesc_by_cid);      my $lastsubonly;
     $request->print($partinfo);  
     $request->print($lastsubonly);  
   
       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 {
           $lastsubonly =
               '<div class="LC_grade_submissions_body">'
              .'<b>'.&mt('Date Submitted:').'</b> '.$$timestamp."\n";
   
    my %seenparts;
    my @part_response_id = &flatten_responseType($responseType);
    foreach my $part (@part_response_id) {
       my ($partid,$respid) = @{ $part };
       my $display_part=&get_display_part($partid,$symb);
       if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
    if (exists($seenparts{$partid})) { next; }
    $seenparts{$partid}=1;
                   $request->print(
                       '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                       ' <b>'.&mt('Collaborative submission by: [_1]',
                                  '<a href="javascript:viewSubmitter(\''.
                                  $env{"form.$uname:$udom:$partid:submitted_by"}.
                                  '\');" target="_self">'.
                                  $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a>').
                       '<br />');
    next;
       }
       my $responsetype = $responseType->{$partid}->{$respid};
       if (!exists($record{"resource.$partid.$respid.submission"})) {
                   $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.
                       '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                       ' <span class="LC_internal_info">'.
                       '('.&mt('Response ID: [_1]',$respid).')'.
                       '</span>&nbsp; &nbsp;'.
             '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';
    next;
       }
       foreach my $submission (@$string) {
    my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
    if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
    my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4);
    # Similarity check
                   my $similar='';
                   my ($type,$trial,$rndseed);
                   if ($hide eq 'rand') {
                       $type = 'randomizetry';
                       $trial = $record{"resource.$partid.tries"};
                       $rndseed = $record{"resource.$partid.rndseed"};
                   }
           if ($env{'form.checkPlag'}) {
       my ($oname,$odom,$ocrsid,$oessay,$osim)=
       &most_similar($uname,$udom,$symb,$subval);
       if ($osim) {
    $osim=int($osim*100.0);
                           if ($hide eq 'anon') {
                               $similar='<hr /><span class="LC_warning">'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'<br />'.
                                        &mt('As the current submission is for an anonymous survey, no other details are available.').'</span><hr />';
                           } else {
       $similar='<hr />';
                               if ($essayurl eq 'lib/templates/simpleproblem.problem') {
                                   $similar .= '<h3><span class="LC_warning">'.
                                               &mt('Essay is [_1]% similar to an essay by [_2]',
                                                   $osim,
                                                   &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
                                               '</span></h3>';
                               } 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 (($env{'form.lastSub'} eq 'lastonly') ||
                       ($env{'form.lastSub'} eq 'datesub')  ||
                       ($env{'form.lastSub'} =~ /^(last|all)$/)) {
       my $display_part=&get_display_part($partid,$symb);
                       $lastsubonly.='<div class="LC_grade_submission_part">'.
                           '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                           ' <span class="LC_internal_info">'.
                           '('.&mt('Response ID: [_1]',$respid).')'.
                           '</span>&nbsp; &nbsp;';
       my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);
       if (@$files) {
                           if ($hide eq 'anon') {
                               $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));
                           } else {
                               $lastsubonly.='<br /><br />'.'<b>'.&mt('Submitted Files:').'</b>'
                                           .'<br /><span class="LC_warning">';
                               if(@$files == 1) {
                                   $lastsubonly .= &mt('Like all files provided by users, this file may contain viruses!');
                               } else {
                                   $lastsubonly .= &mt('Like all files provided by users, these files may contain viruses!');
                               }
                               $lastsubonly .= '</span>';
                               foreach my $file (@$files) {
                                   &Apache::lonnet::allowuploaded('/adm/grades',$file);
                                   $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" alt="" /> '.$file.'</a>';
                               }
                           }
    $lastsubonly.='<br />';
                       }
                       if ($hide eq 'anon') {
                           $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>'; 
                       } else {
                           $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>';
                           if ($draft) {
                               $lastsubonly.= ' <span class="LC_warning">'.&mt('Draft Copy').'</span>';
                           }
                           $subval =
       &cleanRecord($subval,$responsetype,$symb,$partid,
    $respid,\%record,$order,undef,$uname,$udom,$type,$trial,$rndseed);
                           if ($responsetype eq 'essay') {
                               $subval =~ s{\n}{<br />}g;
                           }
                           $lastsubonly.=$subval."\n";
                       }
                       if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
       $lastsubonly.='</div>';
    }
               }
    }
    $lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body
       }
       $request->print($lastsubonly);
     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 2737  sub submission {
  .$udom.'" />'."\n");   .$udom.'" />'."\n");
     # return if view submission with no grading option      # return if view submission with no grading option
     if (!&canmodify($usec)) {      if (!&canmodify($usec)) {
         $request->print('<p><span class="LC_warning">'.&mt('No grading privileges').'</span></p></div>');   $request->print('<p><span class="LC_warning">'.&mt('No grading privileges').'</span></p></div>');
         return;   return;
     } else {      } else {
  $request->print('</div>'."\n");   $request->print('</div>'."\n");
     }      }
Line 3520  sub submission { Line 2758  sub submission {
         $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript          $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript
         $result.='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".          $result.='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
                  '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".                   '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".
          '&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.                   '&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
                  ',\''.$msgfor.'\');" target="_self">'.                   ',\''.$msgfor.'\');" target="_self">'.
                  &mt('Compose message to student'.(scalar(@$col_fullnames) >= 1 ? 's' : '')).'</a><label> ('.                   &mt('Compose message to student'.(scalar(@$col_fullnames) >= 1 ? 's' : '')).'</a><label> ('.
                  &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.                   &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.
Line 3528  sub submission { Line 2766  sub submission {
                  '/mailbkgrd.gif" width="14" height="10" alt="" name="mailicon'.$counter.'" />'."\n".                   '/mailbkgrd.gif" width="14" height="10" alt="" name="mailicon'.$counter.'" />'."\n".
                  '<br />&nbsp;('.                   '<br />&nbsp;('.
                  &mt('Message will be sent when you click on Save &amp; Next below.').")\n".                   &mt('Message will be sent when you click on Save &amp; Next below.').")\n".
          '</div></div>';                   '</div></div>';
         $request->print($result);          $request->print($result);
     }      }
   
Line 3606  sub submission { Line 2844  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 3845  sub check_collaborators { Line 2903  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,$is_tool)=@_;
     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 3952  sub get_last_submission { Line 2977  sub get_last_submission {
  $string[0] =   $string[0] =
     '<span class="LC_warning">'.$msg.'</span>';      '<span class="LC_warning">'.$msg.'</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 3007  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 4178  sub processHandGrade { Line 3204  sub processHandGrade {
     }      }
   
     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,undef,undef,\%queueable);
     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 3261  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,\%queueable);
  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 = ();      my %keyhash = ();
Line 4340  sub processHandGrade { Line 3315  sub processHandGrade {
  $ctr = 0;   $ctr = 0;
  while ($ctr < $ngrade) {   while ($ctr < $ngrade) {
     if ($env{'form.newmsg'.$ctr} ne '') {      if ($env{'form.newmsg'.$ctr} ne '') {
  $keyhash{$symb.'_savemsg'.$idx} = $env{'form.newmsg'.$ctr};          $keyhash{$symb.'_savemsg'.$idx} = $env{'form.newmsg'.$ctr};
  $env{'form.savemsg'.$idx} = $env{'form.newmsg'.$ctr};          $env{'form.savemsg'.$idx} = $env{'form.newmsg'.$ctr};
  $idx++;          $idx++;
     }      }
     $ctr++;      $ctr++;
  }   }
Line 4350  sub processHandGrade { Line 3325  sub processHandGrade {
  $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};   $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};
     }      }
     if (($numessay) || ($env{'form.compmsg'})) {      if (($numessay) || ($env{'form.compmsg'})) {
  my $putresult = &Apache::lonnet::put          my $putresult = &Apache::lonnet::put
     ('nohist_handgrade',\%keyhash,$cdom,$cnum);              ('nohist_handgrade',\%keyhash,$cdom,$cnum);
     }      }
   
     # Called by Save & Refresh from Highlight Attribute Window      # Called by Save & Refresh from Highlight Attribute Window
Line 4453  sub processHandGrade { Line 3428  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 3436  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,$queueable) = @_;
         $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 3444  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 3452  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,$domain,$stuname);  
         } 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 3487  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 3497  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 4601  sub saveHandGrade { Line 3554  sub saveHandGrade {
         &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 4675  sub handback_files { Line 3624  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 3654  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 3665  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 3785  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 5171  sub viewgrades { Line 4046  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 4059  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 4073  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 5255  sub viewgrades { Line 4130  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 5317  sub viewgrades { Line 4192  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 5366  sub viewgrades { Line 4241  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 4294  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 4304  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,$is_tool) = @_;
     my ($uname,$udom) = split(/:/,$student);      my ($uname,$udom) = split(/:/,$student);
Line 5566  sub editgrades { Line 4441  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 5620  sub editgrades { Line 4491  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);      my ($got_types,%queueable);
     for ($i=0; $i<$env{'form.total'}; $i++) {      for ($i=0; $i<$env{'form.total'}; $i++) {
  my $user = $env{'form.ctr'.$i};   my $user = $env{'form.ctr'.$i};
  my ($uname,$udom)=split(/:/,$user);   my ($uname,$udom)=split(/:/,$user);
  my %newrecord;   my %newrecord;
  my $updateflag = 0;   my $updateflag = 0;
         my $usec=$classlist->{"$uname:$udom"}[5];   my $usec=$classlist->{"$uname:$udom"}[5];
         my $canmodify = &canmodify($usec);   my $canmodify = &canmodify($usec);
         my $line = '<td'.($canmodify?'':' colspan="2"').'>'.   my $line = '<td'.($canmodify?'':' colspan="2"').'>'.
                    &nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';     &nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';
         if (!$canmodify) {   if (!$canmodify) {
             push(@noupdate,      push(@noupdate,
                  $line."<td colspan=\"$totcolspan\"><span class=\"LC_warning\">".   $line."<td colspan=\"$totcolspan\"><span class=\"LC_warning\">".
                  &mt('Not allowed to modify student')."</span></td>");   &mt('Not allowed to modify student')."</span></td>");
             next;      next;
         }   }
         my %aggregate = ();          my %aggregate = ();
         my $aggregateflag = 0;          my $aggregateflag = 0;
  $user=~s/:/_/; # colon doen't work in javascript for names   $user=~s/:/_/; # colon doen't work in javascript for names
         my (%weights,%awardeds,%excuseds);  
  foreach (@partid) {   foreach (@partid) {
     my $old_aw    = $env{'form.GD_'.$user.'_'.$_.'_awarded_s'};      my $old_aw    = $env{'form.GD_'.$user.'_'.$_.'_awarded_s'};
     my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);      my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);
Line 5648  sub editgrades { Line 4518  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 4558  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 4575  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 5749  sub editgrades { Line 4616  sub editgrades {
         } else {          } else {
     $none_graded = 0;      $none_graded = 0;
         }          }
                     }      }
  }                  }
   
  if ($all_graded || $none_graded) {   if ($all_graded || $none_graded) {
     &Apache::bridgetask::remove_from_queue('gradingqueue',      &Apache::bridgetask::remove_from_queue('gradingqueue',
    $symb,$cdom,$cnum,     $symb,$cdom,$cnum,
Line 5763  sub editgrades { Line 4629  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 5820  sub split_part_type { Line 4681  sub split_part_type {
 #  #
 #--- Javascript to handle csv upload  #--- Javascript to handle csv upload
 sub csvupload_javascript_reverse_associate {  sub csvupload_javascript_reverse_associate {
     my $error1=&mt('You need to specify the username or the student/employee ID');      my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
     my $error2=&mt('You need to specify at least one grading field');      my $error2=&mt('You need to specify at least one grading field');
   &js_escape(\$error1);    &js_escape(\$error1);
   &js_escape(\$error2);    &js_escape(\$error2);
Line 5829  sub csvupload_javascript_reverse_associa Line 4690  sub csvupload_javascript_reverse_associa
     var foundsomething=0;      var foundsomething=0;
     var founduname=0;      var founduname=0;
     var foundID=0;      var foundID=0;
       var foundclicker=0;
     for (i=0;i<=vf.nfields.value;i++) {      for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');        tw=eval('vf.f'+i+'.selectedIndex');
       if (i==0 && tw!=0) { foundID=1; }        if (i==0 && tw!=0) { foundID=1; }
       if (i==1 && tw!=0) { founduname=1; }        if (i==1 && tw!=0) { founduname=1; }
       if (i!=0 && i!=1 && i!=2 && tw!=0) { foundsomething=1; }        if (i==2 && tw!=0) { foundclicker=1; }
         if (i!=0 && i!=1 && i!=2 && i!=3 && tw!=0) { foundsomething=1; }
     }      }
     if (founduname==0 && foundID==0) {      if (founduname==0 && foundID==0 && foundclicker==0) {
  alert('$error1');   alert('$error1');
  return;   return;
     }      }
Line 5862  ENDPICK Line 4725  ENDPICK
 }  }
   
 sub csvupload_javascript_forward_associate {  sub csvupload_javascript_forward_associate {
     my $error1=&mt('You need to specify the username or the student/employee ID');      my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
     my $error2=&mt('You need to specify at least one grading field');      my $error2=&mt('You need to specify at least one grading field');
   &js_escape(\$error1);    &js_escape(\$error1);
   &js_escape(\$error2);    &js_escape(\$error2);
Line 5871  sub csvupload_javascript_forward_associa Line 4734  sub csvupload_javascript_forward_associa
     var foundsomething=0;      var foundsomething=0;
     var founduname=0;      var founduname=0;
     var foundID=0;      var foundID=0;
       var foundclicker=0;
     for (i=0;i<=vf.nfields.value;i++) {      for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');        tw=eval('vf.f'+i+'.selectedIndex');
       if (tw==1) { foundID=1; }        if (tw==1) { foundID=1; }
       if (tw==2) { founduname=1; }        if (tw==2) { founduname=1; }
       if (tw>3) { foundsomething=1; }        if (tw==3) { foundclicker=1; }
         if (tw>4) { foundsomething=1; }
     }      }
     if (founduname==0 && foundID==0) {      if (founduname==0 && foundID==0 && Æ’oundclicker==0) {
  alert('$error1');   alert('$error1');
  return;   return;
     }      }
Line 5909  sub csvuploadmap_header { Line 4774  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 4782  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 5951  sub csvupload_fields { Line 4813  sub csvupload_fields {
   
     my @fields=(['ID','Student/Employee ID'],      my @fields=(['ID','Student/Employee ID'],
  ['username','Student Username'],   ['username','Student Username'],
    ['clicker','Clicker ID'],
  ['domain','Student Domain']);   ['domain','Student Domain']);
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     foreach my $part (sort(@parts)) {      foreach my $part (sort(@parts)) {
  my @datum;   my @datum;
  my $display=&Apache::lonnet::metadata($url,$part.'.display',$toolsymb);   my $display=&Apache::lonnet::metadata($url,$part.'.display',$toolsymb);
  my $name=$part;   my $name=$part;
  if  (!$display) { $display = $name; }   if (!$display) { $display = $name; }
  @datum=($name,$display);   @datum=($name,$display);
  if ($name=~/^stores_(.*)_awarded/) {   if ($name=~/^stores_(.*)_awarded/) {
     push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);      push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);
Line 6013  sub upcsvScores_form { Line 4876  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 6033  sub csvuploadmap { Line 4895  sub csvuploadmap {
     if (!$env{'form.datatoken'}) {      if (!$env{'form.datatoken'}) {
  $datatoken=&Apache::loncommon::upfile_store($request);   $datatoken=&Apache::loncommon::upfile_store($request);
     } else {      } else {
         $datatoken=&Apache::loncommon::valid_datatoken($env{'form.datatoken'});   $datatoken=&Apache::loncommon::valid_datatoken($env{'form.datatoken'});
         if ($datatoken ne '') {           if ($datatoken ne '') {
     &Apache::loncommon::load_tmp_file($request,$datatoken);      &Apache::loncommon::load_tmp_file($request,$datatoken);
         }          }
     }      }
     my @records=&Apache::loncommon::upfile_record_sep();      my @records=&Apache::loncommon::upfile_record_sep();
     if ($env{'form.noFirstLine'}) { shift(@records); }  
     &csvuploadmap_header($request,$symb,$datatoken,$#records+1);      &csvuploadmap_header($request,$symb,$datatoken,$#records+1);
     my ($i,$keyfields);      my ($i,$keyfields);
     if (@records) {      if (@records) {
Line 6076  sub csvuploadmap { Line 4937  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 4950  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 6129  sub csvuploadassign { Line 4988  sub csvuploadassign {
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $error_msg = '';      my $error_msg = '';
     my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'});      my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'});
     if ($datatoken ne '') {      if ($datatoken ne '') { 
         &Apache::loncommon::load_tmp_file($request,$datatoken);          &Apache::loncommon::load_tmp_file($request,$datatoken);
     }      }
     my @gradedata = &Apache::loncommon::upfile_record_sep();      my @gradedata = &Apache::loncommon::upfile_record_sep();
     if ($env{'form.noFirstLine'}) { shift(@gradedata); }  
     my %fields=&get_fields();      my %fields=&get_fields();
     my $courseid=$env{'request.course.id'};      my $courseid=$env{'request.course.id'};
     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};  
     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};  
     my ($classlist) = &getclasslist('all',0);      my ($classlist) = &getclasslist('all',0);
     my @notallowed;      my @notallowed;
     my @skipped;      my @skipped;
     my @warnings;      my @warnings;
     my $countdone=0;      my $countdone=0;
     my @parts;  
     my %needpb = &passbacks_for_symb($cdom,$cnum,$symb);  
     my $passback;  
     if (keys(%needpb)) {  
         $passback = 1;  
         my $navmap = Apache::lonnavmaps::navmap->new();  
         if (ref($navmap)) {  
             my $res = $navmap->getBySymb($symb);  
             if (ref($res)) {  
                 my $partlist = $res->parts();  
                 if (ref($partlist) eq 'ARRAY') {  
                     @parts = sort(@{$partlist});  
                 }  
             }  
         } else {  
             return &navmap_errormsg();  
         }  
     }  
     my (%skip_passback,%pbsave,%weights,%awardeds,%excuseds);  
   
     foreach my $grade (@gradedata) {      foreach my $grade (@gradedata) {
  my %entries=&Apache::loncommon::record_sep($grade);   my %entries=&Apache::loncommon::record_sep($grade);
  my $domain;   my $domain;
Line 6177  sub csvuploadassign { Line 5013  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 5073  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 5095  sub csvuploadassign {
  if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }   if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }
  if ($dest=~/stores_(.*)_solved/)  { if ($points{$1}) {next;} }   if ($dest=~/stores_(.*)_solved/)  { if ($points{$1}) {next;} }
  my $store_key=$dest;   my $store_key=$dest;
                 if ($passback) {  
                     if ($store_key=~/stores_(.*)_(awarded|solved)/) {  
                         my ($part,$key) = ($1,$2);  
                         unless ((ref($weights{$symb}) eq 'HASH') && (exists($weights{$symb}{$part}))) {  
                             $weights{$symb}{$part} = &Apache::lonnet::EXT('resource.'.$part.'.weight',  
                                                                           $symb,$domain,$username);  
                         }  
                         if ($key eq 'awarded') {  
                             $awardeds{$symb}{$part} = $entries{$fields{$dest}};  
                         } elsif ($key eq 'solved') {  
                             if ($entries{$fields{$dest}} =~ /^excused/) {  
                                 $excuseds{$symb}{$part} = 1;  
                             }  
                         }  
                     }  
                 }  
  $store_key=~s/^stores/resource/;   $store_key=~s/^stores/resource/;
  $store_key=~s/_/\./g;   $store_key=~s/_/\./g;
  $grades{$store_key}=$entries{$fields{$dest}};   $grades{$store_key}=$entries{$fields{$dest}};
Line 6264  sub csvuploadassign { Line 5111  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 6364  LISTJAVASCRIPT Line 5190  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 5239  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 6516  sub displayPage { Line 5342  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 6602  sub displayPage { Line 5428  sub displayPage {
             } else {              } else {
         if ($env{'form.vProb'} eq 'yes' ) {          if ($env{'form.vProb'} eq 'yes' ) {
     $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,      $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
            undef,'both',\%form);           undef,'both',\%form);
         } else {          } else {
     my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);      my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);
     $companswer =~ s|<form(.*?)>||g;      $companswer =~ s|<form(.*?)>||g;
Line 6613  sub displayPage { Line 5439  sub displayPage {
 #    }  #    }
 #    $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;  #    $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
     $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>'.&mt('Correct answer').':</b><br />'.$companswer;      $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);
Line 6637  sub displayPage { Line 5463  sub displayPage {
  $responseType{$partid} = \%responseIds;   $responseType{$partid} = \%responseIds;
     }      }
     $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);      $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);
   
  }   }
     } elsif ($env{'form.lastSub'} eq 'all') {      } elsif ($env{'form.lastSub'} eq 'all') {
  my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');   my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
                 my $identifier = (&canmodify($usec)? $prob : '');                  my $identifier = (&canmodify($usec)? $prob : ''); 
  $studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,   $studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,
  $env{'request.course.id'},   $env{'request.course.id'},
  '','.submission',undef,   '','.submission',undef,
Line 6700  sub displaySubByDates { Line 5525  sub displaySubByDates {
         if ($is_tool) {          if ($is_tool) {
             return '<br />&nbsp;<span class="LC_warning">'.&mt('No grade passed back.').'</span><br />';              return '<br />&nbsp;<span class="LC_warning">'.&mt('No grade passed back.').'</span><br />';
         } else {          } else {
     return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';              return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';
         }          }
     }      }
   
Line 6734  sub displaySubByDates { Line 5559  sub displaySubByDates {
             if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {              if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {
                 $hidden = 1;                  $hidden = 1;
             }              }
     my @matchKey;              my @matchKey;
             if ($isTask) {              if ($isTask) {
                 @matchKey = sort(grep(/^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys));                  @matchKey = sort(grep(/^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys));
             } elsif ($is_tool) {              } elsif ($is_tool) {
                 @matchKey = sort(grep(/^resource\.\Q$partid\E\.awarded$/,@versionKeys));                  @matchKey = sort(grep(/^resource\.\Q$partid\E\.awarded$/,@versionKeys));
             } else {              } else {
  @matchKey = sort(grep(/^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));                  @matchKey = sort(grep(/^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));
             }              }
 #    next if ($$record{"$version:resource.$partid.solved"} eq '');  #    next if ($$record{"$version:resource.$partid.solved"} eq '');
     my $display_part=&get_display_part($partid,$symb);      my $display_part=&get_display_part($partid,$symb);
Line 6751  sub displaySubByDates { Line 5576  sub displaySubByDates {
                         $displaySub[0].=$$record{"$version:resource.$partid.awarded"};                          $displaySub[0].=$$record{"$version:resource.$partid.awarded"};
                     } else {                      } else {
         my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)          my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
                        : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));                     : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
                         $displaySub[0].='<span class="LC_nobreak">';                          $displaySub[0].='<span class="LC_nobreak">';
                         $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'                          $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'
                                        .' <span class="LC_internal_info">'                                         .' <span class="LC_internal_info">'
Line 6770  sub displaySubByDates { Line 5595  sub displaySubByDates {
         $displaySub[0].=&mt('Trial not counted');          $displaySub[0].=&mt('Trial not counted');
             } else {              } else {
         $displaySub[0].=&mt('Trial: [_1]',          $displaySub[0].=&mt('Trial: [_1]',
             $$record{"$where.$partid.tries"});          $$record{"$where.$partid.tries"});
                                 if (($rndseed ne '')  && ($lastrndseed{$partid} ne '')) {                                  if (($rndseed ne '') && ($lastrndseed{$partid} ne '')) {
                                     if (($rndseed ne $lastrndseed{$partid}) &&                                      if (($rndseed ne $lastrndseed{$partid}) &&
                                         (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) {                                          (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) {
                                         $newvariation = '&nbsp;('.&mt('New variation this try').')';                                          $newvariation = '&nbsp;('.&mt('New variation this try').')';
Line 6779  sub displaySubByDates { Line 5604  sub displaySubByDates {
                                 }                                  }
                                 $lastrndseed{$partid} = $rndseed;                                  $lastrndseed{$partid} = $rndseed;
                                 $lasttype{$partid} = $type;                                  $lasttype{$partid} = $type;
            }              }
            my $responseType=($isTask ? 'Task'              my $responseType=($isTask ? 'Task'
                                                   : $responseType->{$partid}->{$responseId});                                                : $responseType->{$partid}->{$responseId});
            if (!exists($orders{$partid})) { $orders{$partid}={}; }              if (!exists($orders{$partid})) { $orders{$partid}={}; }
            if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {              if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {
        $orders{$partid}->{$responseId}=          $orders{$partid}->{$responseId}=
             &get_order($partid,$responseId,$symb,$uname,$udom,              &get_order($partid,$responseId,$symb,$uname,$udom,
                                                $no_increment,$type,$trial,$rndseed);                                                 $no_increment,$type,$trial,$rndseed);
             }              }
Line 6806  sub displaySubByDates { Line 5631  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"}))) {      } elsif (($is_tool) && (exists($$record{"$version:resource.$partid.solved"}))) {
                 if ($$record{"$version:resource.$partid.solved"} =~ /^(in|)correct_by_passback$/) {   if ($$record{"$version:resource.$partid.solved"} =~ /^(in|)correct_by_passback$/) {
                     $displaySub[1].=&mt('Grade passed back by external tool');      $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) {   unless ($is_tool) {
     $displaySub[2].=' (<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"};
Line 6886  sub updateGradeByPage { Line 5711  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 5719  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 6913  sub updateGradeByPage { Line 5736  sub updateGradeByPage {
                 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}) {
Line 6937  sub updateGradeByPage { Line 5757  sub updateGradeByPage {
                 }                  }
  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 5768  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 5795  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 7022  sub updateGradeByPage { Line 5834  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 5847  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 7439  sub scantron_selectphase { Line 6162  sub scantron_selectphase {
   
     $ssi_error = 0;      $ssi_error = 0;
   
     if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||      if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'}) {
         &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {  
   
         # Chunk of form to prompt for a scantron file upload.   # Chunk of form to prompt for a scantron file upload.
   
         $r->print('          $r->print('
     <br />');      <br />');
         my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
         my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};      my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
         my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');      my $csec= $env{'request.course.sec'};
         &js_escape(\$alertmsg);      my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');
         my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($cdom);      &js_escape(\$alertmsg);
         $r->print(&Apache::lonhtmlcommon::scripttag('      my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($cdom);
       $r->print(&Apache::lonhtmlcommon::scripttag('
     function checkUpload(formname) {      function checkUpload(formname) {
         if (formname.upfile.value == "") {   if (formname.upfile.value == "") {
             alert("'.$alertmsg.'");      alert("'.$alertmsg.'");
             return false;      return false;
         }   }
         formname.submit();   formname.submit();
     }'."\n".$formatjs));      }'."\n".$formatjs));
         $r->print('      $r->print('
               <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">                <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
                 '.$default_form_data.'                  '.$default_form_data.'
                 <input name="courseid" type="hidden" value="'.$cnum.'" />                  <input name="courseid" type="hidden" value="'.$cnum.'" />
                   <input name="coursesec" type="hidden" value="'.$csec.'" />
                 <input name="domainid" type="hidden" value="'.$cdom.'" />                  <input name="domainid" type="hidden" value="'.$cdom.'" />
                 <input name="command" value="scantronupload_save" type="hidden" />                  <input name="command" value="scantronupload_save" type="hidden" />
               '.&Apache::loncommon::start_data_table('LC_scantron_action').'                '.&Apache::loncommon::start_data_table('LC_scantron_action').'
Line 7474  sub scantron_selectphase { Line 6198  sub scantron_selectphase {
               '.&Apache::loncommon::start_data_table_row().'                '.&Apache::loncommon::start_data_table_row().'
             <td>              <td>
                 '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'<br />'."\n");                  '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'<br />'."\n");
         if ($formatoptions) {      if ($formatoptions) {
             $r->print('</td>          $r->print('</td>
                  '.&Apache::loncommon::end_data_table_row().'                   '.&Apache::loncommon::end_data_table_row().'
                  '.&Apache::loncommon::start_data_table_row().'                   '.&Apache::loncommon::start_data_table_row().'
                  <td>'.$formattitle.('&nbsp;'x2).$formatoptions.'                   <td>'.$formattitle.('&nbsp;'x2).$formatoptions.'
Line 7483  sub scantron_selectphase { Line 6207  sub scantron_selectphase {
                  '.&Apache::loncommon::end_data_table_row().'                   '.&Apache::loncommon::end_data_table_row().'
                  '.&Apache::loncommon::start_data_table_row().'                   '.&Apache::loncommon::start_data_table_row().'
                  <td>'                   <td>'
             );          );
         } else {      } else {
             $r->print(' <br />');          $r->print(' <br />');
         }      }
         $r->print('<input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />      $r->print('<input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
               </td>                </td>
              '.&Apache::loncommon::end_data_table_row().'               '.&Apache::loncommon::end_data_table_row().'
              '.&Apache::loncommon::end_data_table().'               '.&Apache::loncommon::end_data_table().'
              </form>'               </form>'
         );      );
   
     }      }
   
Line 7609  sub scantron_selectphase { Line 6333  sub scantron_selectphase {
 =item username_to_idmap  =item username_to_idmap
   
     creates a hash keyed by student/employee ID with values of the corresponding      creates a hash keyed by student/employee ID with values of the corresponding
     student username:domain.      student username:domain. If a single ID occurs for more than one student,
       the status of the student is checked, and if Active, the value in the hash
       will be set to the Active student.
   
   Arguments:    Arguments:
   
Line 7809  sub digits_to_letters { Line 6535  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 6565  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 6658  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 6672  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 8007  sub scantron_parse_scanline { Line 6733  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,$need_symb_in_map,$symb_for_examcode) = @_;
     return unless ((ref($resources) eq 'ARRAY') && (ref($master_seq) eq 'ARRAY') &&      return unless ((ref($resources) eq 'ARRAY') && (ref($master_seq) eq 'ARRAY') && 
                    (ref($symb_to_resource) eq 'HASH'));                     (ref($symb_to_resource) eq 'HASH'));
     if ($need_symb_in_map) {      if ($need_symb_in_map) {
         return unless (ref($symb_for_examcode) eq 'HASH');          return unless (ref($symb_for_examcode) eq 'HASH');
Line 8096  sub scantron_validator_lettnum { Line 6822  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 8392  sub scantron_process_corrections { Line 7118  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 8529  sub scantron_warning_screen { Line 7255  sub scantron_warning_screen {
             '<tr><td><b>'.&mt('Hand-graded items: points from last bubble in row').'</b></td><td><tt>'.              '<tr><td><b>'.&mt('Hand-graded items: points from last bubble in row').'</b></td><td><tt>'.
             $env{'form.scantron_lastbubblepoints'}.'</tt></td></tr>';              $env{'form.scantron_lastbubblepoints'}.'</tt></td></tr>';
     }      }
     return ('      return '
 <p>  <p>
 <span class="LC_warning">  <span class="LC_warning">
 '.&mt("Please double check the information below before clicking on '[_1]'",&mt($button_text)).'</span>  '.&mt("Please double check the information below before clicking on '[_1]'",&mt($button_text)).'</span>
Line 8541  sub scantron_warning_screen { Line 7267  sub scantron_warning_screen {
 </table>  </table>
 <p> '.&mt("If this information is correct, please click on '[_1]'.",&mt($button_text)).'<br />  <p> '.&mt("If this information is correct, please click on '[_1]'.",&mt($button_text)).'<br />
 '.&mt('If something is incorrect, please return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','<a href="/adm/grades?symb='.$symb.'&command=scantron_selectphase" class="LC_info">','</a>').'</p>  '.&mt('If something is incorrect, please return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','<a href="/adm/grades?symb='.$symb.'&command=scantron_selectphase" class="LC_info">','</a>').'</p>
   ';
 <br />  
 ');  
 }  }
   
 =pod  =pod
Line 8569  sub scantron_do_warning { Line 7293  sub scantron_do_warning {
  }    } 
  if ( $env{'form.scantron_selectfile'} eq '') {   if ( $env{'form.scantron_selectfile'} eq '') {
     $r->print('<p><span class="LC_error">'.&mt("You have not selected a file that contains the student's response data.").'</span></p>');      $r->print('<p><span class="LC_error">'.&mt("You have not selected a file that contains the student's response data.").'</span></p>');
  }    }
  if ( $env{'form.scantron_format'} eq '') {   if ( $env{'form.scantron_format'} eq '') {
     $r->print('<p><span class="LC_error">'.&mt("You have not selected the format of the student's response data.").'</span></p>');      $r->print('<p><span class="LC_error">'.&mt("You have not selected the format of the student's response data.").'</span></p>');
  }    }
     } else {      } else {
  my $warning=&scantron_warning_screen('Grading: Validate Records',$symb);   my $warning=&scantron_warning_screen('Grading: Validate Records',$symb);
           my ($checksec,@possibles) = &gradable_sections();
           my $gradesections;
           if ($checksec) {
               my $file=$env{'form.scantron_selectfile'};
               if (&valid_file($file)) {
                   my %bysec = &scantron_get_sections();
                   my $table;
                   if ((keys(%bysec) > 1) || ((keys(%bysec) == 1) && ((keys(%bysec))[0] ne $checksec))) {
                       $gradesections = &mt('Your current role is for section [_1].','<i>'.$checksec.'</i>').'<br />';
                       $table = &Apache::loncommon::start_data_table()."\n".
                                &Apache::loncommon::start_data_table_header_row().
                                '<th>'.&mt('Section').'</th><th>'.&mt('Number of records').'</th>'.
                                 &Apache::loncommon::end_data_table_header_row()."\n";
                       if ($bysec{'none'}) {
                           $table .= &Apache::loncommon::start_data_table_row().
                                     '<td>'.&mt('None').'</td><td>'.$bysec{'none'}.'</td>'.
                                     &Apache::loncommon::end_data_table_row()."\n";
                       }
                       foreach my $sec (sort { $a <=> $b } keys(%bysec)) {
                           next if ($sec eq 'none');
                           $table .= &Apache::loncommon::start_data_table_row().
                                     '<td>'.$sec.'</td><td>'.$bysec{$sec}.'</td>'.
                                     &Apache::loncommon::end_data_table_row()."\n";
                       }
                       $table .= &Apache::loncommon::end_data_table()."\n";
                       $gradesections .= &mt('Sections represented in the bubblesheet data file (based on bubbled student IDs) are as follows:').
                                         '<p>'.$table.'</p>';
                       if (@possibles) {
                           $gradesections .= '<p>'.
                                             &mt('You have role(s) in [quant,_1,other section,other sections] with privileges to manage grades.',
                                                 scalar(@possibles)).'<br />'.
                                             &mt('Check which of those section(s), in addition to section [_1], you wish to grade using this bubblesheet file:',
                                                 '<i>'.$checksec.'</i>').' ';
                           foreach my $sec (sort {$a <=> $b } @possibles) {
                               $gradesections .= '<label><input type="checkbox" name="scantron_othersections" value="'.$sec.'" />'.$sec.'</label>'.('&nbsp;'x2);
                           }
                           $gradesections .= '</p>';
                       }
                   }
               } else {
                   $gradesections = '<p class="LC_error">'.&mt('The selected file is unavailable').'</p>';
               }
           }
         my $bubbledbyhand=&hand_bubble_option();          my $bubbledbyhand=&hand_bubble_option();
  $r->print('   $r->print('
 '.$warning.$bubbledbyhand.'  '.$warning.$gradesections.$bubbledbyhand.'
 <input type="submit" name="submit" value="'.&mt('Grading: Validate Records').'" />  <input type="submit" name="submit" value="'.&mt('Grading: Validate Records').'" />
 <input type="hidden" name="command" value="scantron_validate" />  <input type="hidden" name="command" value="scantron_validate" />
 ');  ');
Line 8664  sub scantron_validate_file { Line 7431  sub scantron_validate_file {
     if ($env{'form.scantron_corrections'}) {      if ($env{'form.scantron_corrections'}) {
  &scantron_process_corrections($r);   &scantron_process_corrections($r);
     }      }
     $r->print('<p>'.&mt('Gathering necessary information.').'</p>');$r->rflush();  
       $r->print('<p>'.&mt('Gathering necessary information.').'</p>');
       my ($checksec,@gradable);
       if ($env{'request.course.sec'}) {
           ($checksec,my @possibles) = &gradable_sections();
           if ($checksec) {
               if (@possibles) {
                   my @chosensecs = &Apache::loncommon::get_env_multiple('form.scantron_othersections');
                   if (@chosensecs) {
                       foreach my $sec (@chosensecs) {
                           if (grep(/^\Q$sec\E$/,@possibles)) {
                               unless (grep(/^\Q$sec\E$/,@gradable)) {
                                   push(@gradable,$sec);
                               }
                           }
                       }
                   }
               }
               $r->print('<p><table>');
               if (@gradable) {
                   my @showsections = sort { $a <=> $b } (@gradable,$checksec);
                   $r->print(
                       '<tr><td><b>'.&mt('Sections to be Graded:').'</b></td><td>'.join(', ',@showsections).'</td></tr>');
               } else {
                   $r->print(
                       '<tr><td><b>'.&mt('Section to be Graded:').'</b></td><td>'.$checksec.'</td></tr>');
               }
               $r->print('</table></p>');
           }
       }
       $r->rflush();
   
     #get the student pick code ready      #get the student pick code ready
     $r->print(&Apache::loncommon::studentbrowser_javascript());      $r->print(&Apache::loncommon::studentbrowser_javascript());
     my $nav_error;      my $nav_error;
Line 8689  sub scantron_validate_file { Line 7487  sub scantron_validate_file {
  $env{'form.validatepass'} = 0;   $env{'form.validatepass'} = 0;
     }      }
     my $currentphase=$env{'form.validatepass'};      my $currentphase=$env{'form.validatepass'};
       my %skipbysec=();
   
     my $stop=0;      my $stop=0;
     while (!$stop && $currentphase < scalar(@validate_phases)) {      while (!$stop && $currentphase < scalar(@validate_phases)) {
  $r->print(&mt('Validating '.$validate_phases[$currentphase]).'<br />');   $r->print(&mt('Validating '.$validate_phases[$currentphase]).'<br />');
  $r->rflush();   $r->rflush();
        
  my $which="scantron_validate_".$validate_phases[$currentphase];   my $which="scantron_validate_".$validate_phases[$currentphase];
  {   {
     no strict 'refs';      no strict 'refs';
     ($stop,$currentphase)=&$which($r,$currentphase);              my @extras=();
               if ($validate_phases[$currentphase] eq 'ID') {
                   @extras = (\%skipbysec,$checksec,@gradable);
               }
       ($stop,$currentphase)=&$which($r,$currentphase,@extras);
  }   }
     }      }
     if (!$stop) {      if (!$stop) {
  my $warning=&scantron_warning_screen('Start Grading',$symb);   my $warning=&scantron_warning_screen('Start Grading',$symb);
           my $secinfo;
           if (keys(%skipbysec) > 0) {
               my $seclist = '<ul>';
               foreach my $sec (sort { $a <=> $b } keys(%skipbysec)) {
                   $seclist .= '<li>'.&mt('section [_1]: [_2]',$sec,$skipbysec{$sec}).'</li>';
               }
               $seclist .= '</ul>';
               $secinfo = '<p class="LC_info">'.
                          &mt('Numbers of records for students in sections not being graded [_1]',
                              $seclist).
                          '</p>';
           }
  $r->print(&mt('Validation process complete.').'<br />'.   $r->print(&mt('Validation process complete.').'<br />'.
                   $warning.                    $secinfo.$warning.
                   &mt('Perform verification for each student after storage of submissions?').                    &mt('Perform verification for each student after storage of submissions?').
                   '&nbsp;<span class="LC_nobreak"><label>'.                    '&nbsp;<span class="LC_nobreak"><label>'.
                   '<input type="radio" name="verifyrecord" value="1" />'.&mt('Yes').'</label>'.                    '<input type="radio" name="verifyrecord" value="1" />'.&mt('Yes').'</label>'.
Line 8725  sub scantron_validate_file { Line 7539  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 7918  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 9120  sub scantron_validate_sequence { Line 7935  sub scantron_validate_sequence {
   
   
 sub scantron_validate_ID {  sub scantron_validate_ID {
     my ($r,$currentphase) = @_;      my ($r,$currentphase,$skipbysec,$checksec,@gradable) = @_;
           
     #get student info      #get student info
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
       my $secidx = &Apache::loncoursedata::CL_SECTION();
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
Line 9138  sub scantron_validate_ID { Line 7954  sub scantron_validate_ID {
     }      }
   
     my %found=('ids'=>{},'usernames'=>{});      my %found=('ids'=>{},'usernames'=>{});
       my $unsavedskips = 0;
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {      for (my $i=0;$i<=$scanlines->{'count'};$i++) {
  my $line=&scantron_get_line($scanlines,$scan_data,$i);   my $line=&scantron_get_line($scanlines,$scan_data,$i);
  if ($line=~/^[\s\cz]*$/) { next; }   if ($line=~/^[\s\cz]*$/) { next; }
Line 9150  sub scantron_validate_ID { Line 7967  sub scantron_validate_ID {
  }   }
  if ($found) {   if ($found) {
     my $username=$idmap{$found};      my $username=$idmap{$found};
               if ($checksec) {
                   if (ref($classlist->{$username}) eq 'ARRAY') {
                       my $stusec = $classlist->{$username}->[$secidx];
                       if ($stusec ne $checksec) {
                           unless ((@gradable > 0) && (grep(/^\Q$stusec\E$/,@gradable))) {
                               my $skip=1;
                               &scantron_put_line($scanlines,$scan_data,$i,$line,$skip);
                               if (ref($skipbysec) eq 'HASH') {
                                   if ($stusec eq '') {
                                       $skipbysec->{'none'} ++;
                                   } else {
                                       $skipbysec->{$stusec} ++;
                                   }
                               }
                               $unsavedskips ++;
                               next;
                           }
                       }
                   }
               }
     if ($found{'ids'}{$found}) {      if ($found{'ids'}{$found}) {
  &scantron_get_correction($r,$i,$scan_record,\%scantron_config,   &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
  $line,'duplicateID',$found);   $line,'duplicateID',$found);
                   if ($unsavedskips) {
                       &scantron_putfile($scanlines,$scan_data);
                       $unsavedskips = 0;
                   }
  return(1,$currentphase);   return(1,$currentphase);
     } elsif ($found{'usernames'}{$username}) {      } elsif ($found{'usernames'}{$username}) {
  &scantron_get_correction($r,$i,$scan_record,\%scantron_config,   &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
  $line,'duplicateID',$username);   $line,'duplicateID',$username);
                   if ($unsavedskips) {
                       &scantron_putfile($scanlines,$scan_data);
                       $unsavedskips = 0;
                   }
  return(1,$currentphase);   return(1,$currentphase);
     }      }
     #FIXME store away line we previously saw the ID on to use above      #FIXME store away line we previously saw the ID on to use above
Line 9165  sub scantron_validate_ID { Line 8010  sub scantron_validate_ID {
  } else {   } else {
     if ($id =~ /^\s*$/) {      if ($id =~ /^\s*$/) {
  my $username=&scan_data($scan_data,"$i.user");   my $username=&scan_data($scan_data,"$i.user");
  if (defined($username) && $found{'usernames'}{$username}) {                  if (($checksec && $username ne '')) {
                       if (ref($classlist->{$username}) eq 'ARRAY') {
                           my $stusec = $classlist->{$username}->[$secidx];
                           if ($stusec ne $checksec) {
                               unless ((@gradable > 0) && (grep(/^\Q$stusec\E$/,@gradable))) {
                                   my $skip=1;
                                   &scantron_put_line($scanlines,$scan_data,$i,$line,$skip);
                                   if (ref($skipbysec) eq 'HASH') {
                                       if ($stusec eq '') {
                                           $skipbysec->{'none'} ++;
                                       } else {
                                           $skipbysec->{$stusec} ++;
                                       }
                                   }
                                   $unsavedskips ++;
                                   next;
                               }
                           }
                       }
    } elsif (defined($username) && $found{'usernames'}{$username}) {
     &scantron_get_correction($r,$i,$scan_record,      &scantron_get_correction($r,$i,$scan_record,
      \%scantron_config,       \%scantron_config,
      $line,'duplicateID',$username);       $line,'duplicateID',$username);
                       if ($unsavedskips) {
                           &scantron_putfile($scanlines,$scan_data);
                           $unsavedskips = 0;
                       }
     return(1,$currentphase);      return(1,$currentphase);
  } elsif (!defined($username)) {   } elsif (!defined($username)) {
     &scantron_get_correction($r,$i,$scan_record,      &scantron_get_correction($r,$i,$scan_record,
      \%scantron_config,       \%scantron_config,
      $line,'incorrectID');       $line,'incorrectID');
                       if ($unsavedskips) {
                           &scantron_putfile($scanlines,$scan_data);
                           $unsavedskips = 0;
                       }
     return(1,$currentphase);      return(1,$currentphase);
  }   }
  $found{'usernames'}{$username}++;   $found{'usernames'}{$username}++;
     } else {      } else {
  &scantron_get_correction($r,$i,$scan_record,\%scantron_config,   &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
  $line,'incorrectID');   $line,'incorrectID');
                   if ($unsavedskips) {
                       &scantron_putfile($scanlines,$scan_data);
                       $unsavedskips = 0;
                   }
  return(1,$currentphase);   return(1,$currentphase);
     }      }
  }   }
     }      }
       if ($unsavedskips) {
           &scantron_putfile($scanlines,$scan_data);
           $unsavedskips = 0;
       }
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
   sub scantron_get_sections {
       my %bysec;
       if ($env{'form.scantron_format'} ne '') {
           my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
           my ($scanlines,$scan_data)=&scantron_getfile();
           my $classlist=&Apache::loncoursedata::get_classlist();
           my %idmap=&username_to_idmap($classlist);
           foreach my $key (keys(%idmap)) {
               my $lckey = lc($key);
               $idmap{$lckey} = $idmap{$key};
           }
           my $secidx = &Apache::loncoursedata::CL_SECTION();
           for (my $i=0;$i<=$scanlines->{'count'};$i++) {
               my $line=&scantron_get_line($scanlines,$scan_data,$i);
               if ($line=~/^[\s\cz]*$/) { next; }
               my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
                                                        $scan_data);
               my $id=lc($$scan_record{'scantron.ID'});
               if (exists($idmap{$id})) {
                   if (ref($classlist->{$idmap{$id}}) eq 'ARRAY') {
                       my $stusec = $classlist->{$idmap{$id}}->[$secidx];
                       if ($stusec eq '') {
                           $bysec{'none'} ++;
                       } else {
                           $bysec{$stusec} ++;
                       }
                   }
               }
           }
       }
       return %bysec;
   }
   
 sub scantron_get_correction {  sub scantron_get_correction {
     my ($r,$i,$scan_record,$scan_config,$line,$error,$arg,      my ($r,$i,$scan_record,$scan_config,$line,$error,$arg,
Line 9228  sub scantron_get_correction { Line 8139  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 8162  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 8260  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 9459  sub questions_to_line_list { Line 8370  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 8401  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 8454  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 8472  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 9600  sub scantron_bubble_selector { Line 8521  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 9930  sub scantron_get_maxbubble { Line 8851  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 8902  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 10073  sub scantron_validate_missingbubbles { Line 8994  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 9007  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 10178  sub scantron_process_students { Line 9099  sub scantron_process_students {
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
   
     my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);      my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config); 
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
Line 10228  sub scantron_process_students { Line 9149  sub scantron_process_students {
 SCANTRONFORM  SCANTRONFORM
     $r->print($result);      $r->print($result);
   
       my ($checksec,@possibles)=&gradable_sections();
     my @delayqueue;      my @delayqueue;
     my (%completedstudents,%scandata);      my (%completedstudents,%scandata);
       
     my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));      my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));
     my $count=&get_todo_count($scanlines,$scan_data);      my $count=&get_todo_count($scanlines,$scan_data);
     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);
Line 10273  SCANTRONFORM Line 9195  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 10290  SCANTRONFORM Line 9212  SCANTRONFORM
      next;       next;
   }    }
         my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION];          my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION];
           if (($checksec ne '') && ($checksec ne $usec)) {
               unless (grep(/^\Q$usec\E$/,@possibles)) {
                   &scantron_add_delay(\@delayqueue,$line,
                                       "No role with manage grades privilege in student's section ($usec)",3);
                   next;
               }
           }
         my $user = $uname.':'.$usec;          my $user = $uname.':'.$usec;
   ($uname,$udom)=split(/:/,$uname);    ($uname,$udom)=split(/:/,$uname);
   
Line 10303  SCANTRONFORM Line 9232  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 10397  SCANTRONFORM Line 9326  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 9419  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 9446  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 9459  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 10595  sub scantron_upload_scantron_data { Line 9524  sub scantron_upload_scantron_data {
   'domainid',    'domainid',
   'coursename',$dom);    'coursename',$dom);
     my $syllabuslink = '<a href="javascript:ToSyllabus();">'.&mt('Syllabus').'</a>'.      my $syllabuslink = '<a href="javascript:ToSyllabus();">'.&mt('Syllabus').'</a>'.
                        ('&nbsp'x2).&mt('(shows course personnel)');                         ('&nbsp'x2).&mt('(shows course personnel)'); 
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
     my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.');      my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.');
     &js_escape(\$nofile_alert);      &js_escape(\$nofile_alert);
Line 10676  END Line 9605  END
             if (keys(%{$domconfig{'scantron'}{'config'}}) > 1) {              if (keys(%{$domconfig{'scantron'}{'config'}}) > 1) {
                 if (($domconfig{'scantron'}{'config'}{'dat'}) &&                  if (($domconfig{'scantron'}{'config'}{'dat'}) &&
                     (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH')) {                      (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH')) {
                     if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {                      if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {  
                         if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {                          if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {
                             my ($onclick,$formatextra,$singleline);                              my ($onclick,$formatextra,$singleline);
                             my @lines = &Apache::lonnet::get_scantronformat_file();                              my @lines = &Apache::lonnet::get_scantronformat_file();
                             my $count = 0;                              my $count = 0;
                             foreach my $line (@lines) {                              foreach my $line (@lines) {
                                 next if (($line =~ /^\#/) || ($line eq ''));                                  next if ($line =~ /^#/);
                                 $singleline = $line;                                  $singleline = $line;
                                 $count ++;                                  $count ++;
                             }                              }
Line 10750  sub scantron_upload_scantron_data_save { Line 9679  sub scantron_upload_scantron_data_save {
  '</form>'."\n";   '</form>'."\n";
     if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&      if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&
  !&Apache::lonnet::allowed('usc',   !&Apache::lonnet::allowed('usc',
     $env{'form.domainid'}.'_'.$env{'form.courseid'})) {      $env{'form.domainid'}.'_'.$env{'form.courseid'}) &&
           !&Apache::lonnet::allowed('usc',
                               $env{'form.domainid'}.'_'.$env{'form.courseid'}.'/'.$env{'form.coursesec'})) {
  $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."<br />");   $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."<br />");
         unless ($symb) {   unless ($symb) {
     $r->print($doanotherupload);      $r->print($doanotherupload);
  }   }
  return '';   return '';
     }      }
     my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});      my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});
     my $uploadedfile;      my $uploadedfile;
     $r->print('<p>'.&mt("Uploading file to [_1]",$coursedata{'description'}).'</p>');      $r->print('<p>'.&mt('Uploading file to [_1]','"'.$coursedata{'description'}.'"').'</p>');
     if (length($env{'form.upfile'}) < 2) {      if (length($env{'form.upfile'}) < 2) {
         $r->print(          $r->print(
             &Apache::lonhtmlcommon::confirm_success(              &Apache::lonhtmlcommon::confirm_success(
Line 10799  sub scantron_upload_scantron_data_save { Line 9730  sub scantron_upload_scantron_data_save {
         my $result =          my $result =
             &Apache::lonnet::userfileupload('upfile','scantron','scantron',$parser,'','',              &Apache::lonnet::userfileupload('upfile','scantron','scantron',$parser,'','',
                                             $env{'form.courseid'},$env{'form.domainid'});                                              $env{'form.courseid'},$env{'form.domainid'});
  if ($result =~ m{^/uploaded/}) {          if ($result =~ m{^/uploaded/}) {
             $r->print(              $r->print(
                 &Apache::lonhtmlcommon::confirm_success(&mt('Upload successful')).'<br />'.                  &Apache::lonhtmlcommon::confirm_success(&mt('Upload successful')).'<br />'.
                 &mt('Uploaded [_1] bytes of data into location: [_2]',                  &mt('Uploaded [_1] bytes of data into location: [_2]',
                         (length($env{'form.upfile'})-1),                          (length($env{'form.upfile'})-1),
                         '<span class="LC_filename">'.$result.'</span>'));                          '<span class="LC_filename">'.$result.'</span>'));
             ($uploadedfile) = ($result =~ m{/([^/]+)$});              ($uploadedfile) = ($result =~ m{/([^/]+)$});
               if ($uploadedfile =~ /^scantron_orig_/) {
                   my $logname = $uploadedfile;
                   $logname =~ s/^scantron_orig_//;
                   if ($logname ne '') {
                       my $now = time;
                       my %info = ($logname => { $now => $env{'user.name'}.':'.$env{'user.domain'} });  
                       &Apache::lonnet::put('scantronupload',\%info,$env{'form.domainid'},$env{'form.courseid'});
                   }
               }
             $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},              $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},
                                                        $env{'form.courseid'},$uploadedfile));                                                         $env{'form.courseid'},$symb,$uploadedfile));
  } else {          } else {
             $r->print(              $r->print(
                 &Apache::lonhtmlcommon::confirm_success(&mt('Upload failed'),1).'<br />'.                  &Apache::lonhtmlcommon::confirm_success(&mt('Upload failed'),1).'<br />'.
                     &mt('An error ([_1]) occurred when attempting to upload the file: [_2]',                      &mt('An error ([_1]) occurred when attempting to upload the file: [_2]',
Line 10825  sub scantron_upload_scantron_data_save { Line 9765  sub scantron_upload_scantron_data_save {
 }  }
   
 sub validate_uploaded_scantron_file {  sub validate_uploaded_scantron_file {
     my ($cdom,$cname,$fname) = @_;      my ($cdom,$cname,$symb,$fname,$context,$countsref) = @_;
   
     my $scanlines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.$fname);      my $scanlines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.$fname);
     my @lines;      my @lines;
     if ($scanlines ne '-1') {      if ($scanlines ne '-1') {
         @lines=split("\n",$scanlines,-1);          @lines=split("\n",$scanlines,-1);
     }      }
     my $output;      my ($output,$secidx,$checksec,$priv,%crsroleshash,@possibles);
       $secidx = &Apache::loncoursedata::CL_SECTION();
       if ($context eq 'download') {
           $priv = 'mgr';
       } else {
           $priv = 'usc';
       }
       unless ((&Apache::lonnet::allowed($priv,$env{'request.role.domain'})) ||
               (($env{'request.course.id'}) &&
                (&Apache::lonnet::allowed($priv,$env{'request.course.id'})))) {
           if ($env{'request.course.sec'} ne '') {
               unless (&Apache::lonnet::allowed($priv,
                                            "$env{'request.course.id'}/$env{'request.course.sec'}")) {
                   unless ($context eq 'download') {
                       $output = '<p class="LC_warning">'.&mt('You do not have permission to upload bubblesheet data').'</p>';
                   }
                   return $output;
               }
               ($checksec,@possibles)=&gradable_sections();
           }
       }
     if (@lines) {      if (@lines) {
         my (%counts,$max_match_format);          my (%counts,$max_match_format);
         my ($found_match_count,$max_match_count,$max_match_pct) = (0,0,0);          my ($found_match_count,$max_match_count,$max_match_pct) = (0,0,0);
Line 10844  sub validate_uploaded_scantron_file { Line 9805  sub validate_uploaded_scantron_file {
         my %unique_formats;          my %unique_formats;
         my @formatlines = &Apache::lonnet::get_scantronformat_file();          my @formatlines = &Apache::lonnet::get_scantronformat_file();
         foreach my $line (@formatlines) {          foreach my $line (@formatlines) {
             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 10861  sub validate_uploaded_scantron_file { Line 9822  sub validate_uploaded_scantron_file {
             %{$counts{$key}} = (              %{$counts{$key}} = (
                                'found'   => 0,                                 'found'   => 0,
                                'total'   => 0,                                 'total'   => 0,
                                  'totalanysec' => 0,
                                  'othersec' => 0,
                               );                                );
             foreach my $line (@lines) {              foreach my $line (@lines) {
                 next if ($line =~ /^#/);                  next if ($line =~ /^#/);
Line 10868  sub validate_uploaded_scantron_file { Line 9831  sub validate_uploaded_scantron_file {
                 my $id = substr($line,$idstart-1,$idlength);                  my $id = substr($line,$idstart-1,$idlength);
                 $id = lc($id);                  $id = lc($id);
                 if (exists($idmap{$id})) {                  if (exists($idmap{$id})) {
                       if ($checksec ne '') {
                           $counts{$key}{'totalanysec'} ++;
                           if (ref($classlist->{$idmap{$id}}) eq 'ARRAY') {
                               my $stusec = $classlist->{$idmap{$id}}->[$secidx];
                               if ($stusec ne $checksec) {
                                   if (@possibles) {
                                       unless (grep(/^\Q$stusec\E$/,@possibles)) {
                                           $counts{$key}{'othersec'} ++;
                                           next;
                                       }
                                   } else {
                                       $counts{$key}{'othersec'} ++;
                                       next;
                                   }
                               }
                           }
                       }
                     $counts{$key}{'found'} ++;                      $counts{$key}{'found'} ++;
                 }                  }
                 $counts{$key}{'total'} ++;                  $counts{$key}{'total'} ++;
Line 10882  sub validate_uploaded_scantron_file { Line 9862  sub validate_uploaded_scantron_file {
                 }                  }
             }              }
         }          }
         if (ref($unique_formats{$max_match_format}) eq 'ARRAY') {          if ((ref($unique_formats{$max_match_format}) eq 'ARRAY') && ($context ne 'download')) {
             my $format_descs;              my $format_descs;
             my $numwithformat = @{$unique_formats{$max_match_format}};              my $numwithformat = @{$unique_formats{$max_match_format}};
             for (my $i=0; $i<$numwithformat; $i++) {              for (my $i=0; $i<$numwithformat; $i++) {
Line 10927  sub validate_uploaded_scantron_file { Line 9907  sub validate_uploaded_scantron_file {
                     '<li>'.&mt('The course roster is not up to date.').'</li>'.                      '<li>'.&mt('The course roster is not up to date.').'</li>'.
                     '</ul>';                      '</ul>';
             }              }
               if (($checksec ne '') && (ref($counts{$max_match_format}) eq 'HASH')) {
                   if ($counts{$max_match_format}{'othersec'}) {
                       my $percent_nongrade = (100*$counts{$max_match_format}{'othersec'})/($counts{$max_match_format}{'totalanysec'});
                       my $showpct = sprintf("%.0f",$percent_nongrade).'%';
                       my $confirmdel = &mt('Are you sure you want to permanently delete this file?');
                       &js_escape(\$confirmdel);
                       $output .= '<p class="LC_warning">'.
                                  &mt('Comparison of student IDs in the uploaded file with the course roster found [_1][quant,_2,match,matches][_3] for students in section(s) for which none of your role(s) have privileges to modify grades',
                                      '<b>',$counts{$max_match_format}{'othersec'},'</b>').
                                  '<br />'.
                                  &mt('Unless you are assigned role(s) which allow modification of grades in additional sections, [_1] of the records in this file will be automatically excluded when you perform bubblesheet grading.','<b>'.$showpct.'</b>').
                                  '</p><p>'.
                                  &mt('If you prefer to delete the file now, use: [_1]').
                                  '<form method="post" name="delupload" action="/adm/grades">'.
                                  '<input type="hidden" name="symb" value="'.$symb.'" />'.
                                  '<input type="hidden" name="domainid" value="'.$cdom.'" />'.
                                  '<input type="hidden" name="courseid" value="'.$cname.'" />'.
                                  '<input type="hidden" name="coursesec" value="'.$env{'request.course.sec'}.'" />'. 
                                  '<input type="hidden" name="uploadedfile" value="'.$fname.'" />'. 
                                  '<input type="hidden" name="command" value="scantronupload_delete" />'.
                                  '<input type="button" name="delbutton" value="'.&mt('Delete Uploaded File').'" onclick="javascript:if (confirm('."'$confirmdel'".')) { document.delupload.submit(); }" />'.
                                  '</form></p>';
                   }
               }
         }          }
     } else {          if (($context eq 'download') && ($checksec ne '')) {
               if ((ref($countsref) eq 'HASH') && (ref($counts{$max_match_format}) eq 'HASH')) {
                   $countsref->{'totalanysec'} = $counts{$max_match_format}{'totalanysec'};
                   $countsref->{'othersec'} = $counts{$max_match_format}{'othersec'};
               }
           } 
       } elsif ($context ne 'download') {
         $output = '<p class="LC_warning">'.&mt('Uploaded file contained no data').'</p>';          $output = '<p class="LC_warning">'.&mt('Uploaded file contained no data').'</p>';
     }      }
     return $output;      return $output;
 }  }
   
   sub gradable_sections {
       my $checksec = $env{'request.course.sec'};
       my @oksecs;
       if ($checksec) {
           my %availablesecs = &sections_grade_privs();
           if (ref($availablesecs{'mgr'}) eq 'ARRAY') {
               foreach my $sec (@{$availablesecs{'mgr'}}) {
                   unless (grep(/^\Q$sec\E$/,@oksecs)) {
                       push(@oksecs,$sec);
                   }
               }
               if (grep(/^all$/,@oksecs)) {
                   undef($checksec);
               }
           }
       }
       return($checksec,@oksecs);
   }
   
   sub sections_grade_privs {
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
       my %availablesecs = (
                             mgr => [],
                             vgr => [],
                             usc => [],
                           );
       my $ccrole = 'cc';
       if ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Community') {
           $ccrole = 'co';
       }
       my %crsroleshash = &Apache::lonnet::get_my_roles($env{'user.name'},$env{'user.domain'},
                                                        'userroles',['active'],
                                                        [$ccrole,'in','cr'],$cdom,1);
       my $crsid = $cnum.':'.$cdom;
       foreach my $item (keys(%crsroleshash)) {
           next unless ($item =~ /^$crsid\:/);
           my ($crsnum,$crsdom,$role,$sec) = split(/\:/,$item);
           my $suffix = "/$cdom/$cnum./$cdom/$cnum";
           if ($sec ne '') {
               $suffix = "/$cdom/$cnum/$sec./$cdom/$cnum/$sec";
           }
           if (($role eq $ccrole) || ($role eq 'in')) {
               foreach my $priv ('mgr','vgr','usc') { 
                   unless (grep(/^all$/,@{$availablesecs{$priv}})) {
                       if ($sec eq '') {
                           $availablesecs{$priv} = ['all'];
                       } elsif ($sec ne $env{'request.course.sec'}) {
                           unless (grep(/^\Q$sec\E$/,@{$availablesecs{$priv}})) {
                               push(@{$availablesecs{$priv}},$sec);
                           }
                       }
                   }
               }
           } elsif ($role =~ m{^cr/}) {
               foreach my $priv ('mgr','vgr','usc') {
                   unless (grep(/^all$/,@{$availablesecs{$priv}})) {
                       if ($env{"user.priv.$role.$suffix"} =~ /:$priv&/) {
                           if ($sec eq '') {
                               $availablesecs{$priv} = ['all'];
                           } elsif ($sec ne $env{'request.course.sec'}) {
                               unless (grep(/^\Q$sec\E$/,@{$availablesecs{$priv}})) {
                                   push(@{$availablesecs{$priv}},$sec);
                               }
                           }
                       }
                   }
               }
           }
       }
       return %availablesecs;
   }
   
   sub scantron_upload_delete {
       my ($r,$symb) = @_;
       my $filename = $env{'form.uploadedfile'};
       if ($filename =~ /^scantron_orig_/) {
           if (&Apache::lonnet::allowed('usc',$env{'form.domainid'}) ||
               &Apache::lonnet::allowed('usc',
                                        $env{'form.domainid'}.'_'.$env{'form.courseid'}) ||
               &Apache::lonnet::allowed('usc',
                                        $env{'form.domainid'}.'_'.$env{'form.courseid'}.'/'.$env{'form.coursesec'})) {
               my $uploadurl = '/uploaded/'.$env{'form.domainid'}.'/'.$env{'form.courseid'}.'/'.$env{'form.uploadedfile'};
               my $retrieval = &Apache::lonnet::getfile($uploadurl);
               if ($retrieval eq '-1') {
                   $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                             &mt('File requested for deletion not found.'));
               } else {
                   $filename =~ s/^scantron_orig_//;
                   if ($filename ne '') {
                       my ($is_valid,$numleft);
                       my %info = &Apache::lonnet::get('scantronupload',[$filename],$env{'form.domainid'},$env{'form.courseid'});
                       if (keys(%info)) {
                           if (ref($info{$filename}) eq 'HASH') {
                               foreach my $timestamp (sort(keys(%{$info{$filename}}))) {
                                   if ($info{$filename}{$timestamp} eq $env{'user.name'}.':'.$env{'user.domain'}) {
                                       $is_valid = 1;
                                       delete($info{$filename}{$timestamp}); 
                                   }
                               }
                               $numleft = scalar(keys(%{$info{$filename}}));
                           }
                       }
                       if ($is_valid) {
                           my $result = &Apache::lonnet::removeuploadedurl($uploadurl);
                           if ($result eq 'ok') {
                               $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion successful')).'<br />');
                               if ($numleft) {
                                   &Apache::lonnet::put('scantronupload',\%info,$env{'form.domainid'},$env{'form.courseid'});
                               } else {
                                   &Apache::lonnet::del('scantronupload',[$filename],$env{'form.domainid'},$env{'form.courseid'});
                               }
                           } else {
                               $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                                         &mt('Result was [_1]',$result));
                           }
                       } else {
                           $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                                     &mt('File requested for deletion was uploaded by a different user.'));
                       }
                   } else {
                       $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                                 &mt('Filename of bubblesheet data file requested for deletion is invalid.'));
                   }
               }
           } else {
               $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'. 
                         &mt('You are not permitted to delete bubblesheet data files from the requested course.'));
           }
       } else {
           $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                             &mt('Filename of bubblesheet data file requested for deletion is invalid.'));
       }
       return;
   }
   
 sub valid_file {  sub valid_file {
     my ($requested_file)=@_;      my ($requested_file)=@_;
     foreach my $filename (sort(&scantron_filenames())) {      foreach my $filename (sort(&scantron_filenames())) {
Line 10956  sub scantron_download_scantron_data { Line 10102  sub scantron_download_scantron_data {
 ');  ');
  return;   return;
     }      }
       my (%uploader,$is_owner,%counts,$percent);
       my %uploader = &Apache::lonnet::get('scantronupload',[$file],$cdom,$cname);
       if (ref($uploader{$file}) eq 'HASH') {
           foreach my $timestamp (sort { $a <=> $b } keys(%{$uploader{$file}})) {
               if ($uploader{$file}{$timestamp} eq $env{'user.name'}.':'.$env{'user.domain'}) {
                   $is_owner = 1;
                   last;
               }
           }
       }
       unless ($is_owner) {
           &validate_uploaded_scantron_file($cdom,$cname,$symb,'scantron_orig_'.$file,'download',\%counts);
           if ($counts{'totalanysec'}) {
               my $percent_othersec = (100*$counts{'othersec'})/($counts{'totalanysec'});
               if ($percent_othersec >= 10) {
                   my $showpct = sprintf("%.0f",$percent_othersec).'%';
                   $r->print('<p class="LC_warning">'.
                             &mt('The original uploaded file includes [_1] or more of records for students for which none of your roles have rights to modify grades, so files are unavailable for download.',$showpct).
                             '</p>');
                   return;
               }
           }
       }
     my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;      my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;
     my $corrected='/uploaded/'.$cdom.'/'.$cname.'/scantron_corrected_'.$file;      my $corrected='/uploaded/'.$cdom.'/'.$cname.'/scantron_corrected_'.$file;
     my $skipped='/uploaded/'.$cdom.'/'.$cname.'/scantron_skipped_'.$file;      my $skipped='/uploaded/'.$cdom.'/'.$cname.'/scantron_skipped_'.$file;
Line 10992  sub checkscantron_results { Line 10161  sub checkscantron_results {
     my %scantron_config =      my %scantron_config =
         &Apache::lonnet::get_scantron_config($env{'form.scantron_format'});          &Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);      my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
     my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&Apache::grades::username_to_idmap($classlist);      my %idmap=&Apache::grades::username_to_idmap($classlist);
     my $navmap=Apache::lonnavmaps::navmap->new();      my $navmap=Apache::lonnavmaps::navmap->new();
Line 11003  sub checkscantron_results { Line 10172  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) {          unless ($randomorder || $randompick) {
Line 11043  sub checkscantron_results { Line 10212  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 10222  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 10334  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 11335  sub verify_scantron_grading { Line 10504  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 10513  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 10527  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 11384  sub grading_menu { Line 10552  sub grading_menu {
   
     my @menu = ({ categorytitle=>'Hand Grading',      my @menu = ({ categorytitle=>'Hand Grading',
             items =>[              items =>[
                         {       linktext => 'Select individual students to grade',                          { linktext => 'Select individual students to grade',
                                 url => $url1a,                      url => $url1a,
                                 permission => $permissions{'either'},                      permission => $permissions{'either'},
                                 icon => 'grade_students.png',                      icon => 'grade_students.png',
                                 linktitle => 'Grade current resource for a selection of students.'                      linktitle => 'Grade current resource for a selection of students.'
                         },                          }, 
                         {       linktext => 'Grade ungraded submissions',                          {       linktext => 'Grade ungraded submissions',
                                 url => $url1b,                                  url => $url1b,
                                 permission => $permissions{'either'},                                  permission => $permissions{'either'},
Line 11409  sub grading_menu { Line 10577  sub grading_menu {
                                 icon => 'grade_PageFolder.png',                                  icon => 'grade_PageFolder.png',
                                 linktitle => 'Grade all resources in current page/sequence/folder for one student.'                                  linktitle => 'Grade all resources in current page/sequence/folder for one student.'
                         },                          },
                         {       linktext => 'Download submitted files',                          {       linktext => 'Download submissions',
                                 url => $url1e,                                  url => $url1e,
                                 permission => $permissions{'either'},                                  permission => $permissions{'either'},
                                 icon => 'download_sub.png',                                  icon => 'download_sub.png',
                                 linktitle => 'Download all files submitted by students.'                                  linktitle => 'Download all students submissions.'
                         }]},                          }]},
                          { categorytitle=>'Automated Grading',                           { categorytitle=>'Automated Grading',
                items =>[                 items =>[
Line 11445  sub grading_menu { Line 10613  sub grading_menu {
   
                     ]                      ]
             });              });
     my $cdom = $env{"course.$env{'request.course.id'}.domain"};  
     my $cnum = $env{"course.$env{'request.course.id'}.num"};  
     my %passback = &Apache::lonnet::dump('nohist_linkprot_passback',$cdom,$cnum);  
     if (keys(%passback)) {  
         $fields{'command'} = 'initialpassback';  
         my $url6 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);  
         push (@{$menu[1]{items}},  
                   { linktext => 'Passback of Scores',  
                     url => $url6,  
                     permission => $permissions{'either'},  
                     icon => 'passback.png',  
                     linktitle => 'Passback scores to launcher CMS for resources accessed via LTI-mediated deep-linking',  
                   });  
     }  
     # Create the menu      # Create the menu
     my $Str;      my $Str;
     $Str .= '<form method="post" action="" name="gradingMenu">';      $Str .= '<form method="post" action="" name="gradingMenu">';
Line 11538  sub submit_options_download { Line 10693  sub submit_options_download {
     &commonJSfunctions($request);      &commonJSfunctions($request);
   
     my $result='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".      my $result='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
         $table."\n".                 $table."\n".
         '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";                 '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
     $result.='      $result.='
 <h2>  <h2>
   '.&mt('Select Students for whom to Download Submitted Files').'    '.&mt('Select Students for whom to Download Submissions').'
 </h2>'.&selectfield(1,$is_tool).'  </h2>'.&selectfield(1,$is_tool).'
                 <input type="hidden" name="command" value="downloadfileslink" />                  <input type="hidden" name="command" value="downloadfileslink" /> 
               <input type="submit" value="'.&mt('Next').' &rarr;" />                <input type="submit" value="'.&mt('Next').' &rarr;" />
             </div>              </div>
           </div>            </div>
Line 11566  sub submit_options { Line 10721  sub submit_options {
     $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,$is_tool).'
                 <input type="hidden" name="command" value="submission" />                  <input type="hidden" name="command" value="submission" /> 
               <input type="submit" value="'.&mt('Next').' &rarr;" />        <input type="submit" value="'.&mt('Next').' &rarr;" />
             </div>              </div>
           </div>            </div>
   </form>';    </form>';
Line 11582  sub selectfield { Line 10737  sub selectfield {
            (&transtatus_options,             (&transtatus_options,
             'select_form_order' => ['yes','incorrect','all']);              'select_form_order' => ['yes','incorrect','all']);
    } else {     } else {
        %options =         %options = 
            (&substatus_options,             (&substatus_options,
             'select_form_order' => ['yes','queued','graded','incorrect','all']);              'select_form_order' => ['yes','queued','graded','incorrect','all']);
    }     }
   
   #    #
   # PrepareClasslist() needs to be called to avoid getting a sections list    # PrepareClasslist() needs to be called to avoid getting a sections list
   # for a different course from the @Sections global in lonstatistics.pm,    # for a different course from the @Sections global in lonstatistics.pm, 
   # populated by an earlier request.    # populated by an earlier request.
   #    #
    &Apache::lonstatistics::PrepareClasslist();     &Apache::lonstatistics::PrepareClasslist();
   
    my $result='<div class="LC_columnSection">     my $result='<div class="LC_columnSection">
     
     <fieldset>      <fieldset>
       <legend>        <legend>
        '.&mt('Sections').'         '.&mt('Sections').'
       </legend>        </legend>
       '.&Apache::lonstatistics::SectionSelect('section','multiple',5).'        '.&Apache::lonstatistics::SectionSelect('section','multiple',5).'
     </fieldset>      </fieldset>
     
     <fieldset>      <fieldset>
       <legend>        <legend>
         '.&mt('Groups').'          '.&mt('Groups').'
       </legend>        </legend>
       '.&Apache::lonstatistics::GroupSelect('group','multiple',5).'        '.&Apache::lonstatistics::GroupSelect('group','multiple',5).'
     </fieldset>      </fieldset>
      
     <fieldset>      <fieldset>
       <legend>        <legend>
         '.&mt('Access Status').'          '.&mt('Access Status').'
Line 11657  sub reset_perm { Line 10812  sub reset_perm {
   
 sub init_perm {  sub init_perm {
     &reset_perm();      &reset_perm();
     foreach my $test_perm ('vgr','mgr','opa') {      foreach my $test_perm ('vgr','mgr','opa','usc') {
   
  my $scope = $env{'request.course.id'};   my $scope = $env{'request.course.id'};
  if (!($perm{$test_perm}=&Apache::lonnet::allowed($test_perm,$scope))) {   if (!($perm{$test_perm}=&Apache::lonnet::allowed($test_perm,$scope))) {
Line 11784  sub process_clicker { Line 10939  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 12187  sub turning_eval { Line 11342  sub turning_eval {
     return ($errormsg,$number);      return ($errormsg,$number);
 }  }
   
   
 sub assign_clicker_grades {  sub assign_clicker_grades {
     my ($r,$symb) = @_;      my ($r,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
Line 12196  sub assign_clicker_grades { Line 11352  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().
Line 12262  sub assign_clicker_grades { Line 11414  sub assign_clicker_grades {
                       &mt('More than one entry found for [_1]!','<tt>'.$user.'</tt>').                        &mt('More than one entry found for [_1]!','<tt>'.$user.'</tt>').
                       '</span><br />';                        '</span><br />';
           }            }
           $users{$user}=1;            $users{$user}=1; 
           my @answer=split(/\,/,$env{$key});            my @answer=split(/\,/,$env{$key});
           my $sum=0;            my $sum=0;
           my $realnumber=$number;            my $realnumber=$number;
Line 12309  sub assign_clicker_grades { Line 11461  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 12346  sub startpage { Line 11489  sub startpage {
          $args{'add_entries'} = \%loaditems;           $args{'add_entries'} = \%loaditems;
     }      }
     if ($nomenu) {      if ($nomenu) {
         $args{'only_body'} = 1;          $args{'only_body'} = 1; 
         $r->print(&Apache::loncommon::start_page("Student's Version",$head_extra,\%args));          $r->print(&Apache::loncommon::start_page("Student's Version",$head_extra,\%args));
     } else {      } else {
         if ($env{'request.course.id'}) {          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"});
         }          }
         $args{'bread_crumbs'} = $crumbs;          $args{'bread_crumbs'} = $crumbs;
         $r->print(&Apache::loncommon::start_page('Grading',$head_extra,\%args));          $r->print(&Apache::loncommon::start_page('Grading',$head_extra,\%args));
           if ($env{'request.course.id'}) {
               &Apache::lonquickgrades::startGradeScreen($r,($env{'form.symb'}?'probgrading':'grading'));
           }
     }      }
     unless ($nodisplayflag) {      unless ($nodisplayflag) {
        $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp,$divforres));          $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp,$divforres));
     }      }
 }  }
   
 sub select_problem {  sub select_problem {
     my ($r)=@_;      my ($r)=@_;
     $r->print('<h3>'.&mt('Select the problem or one of the problems you want to grade').'</h3><form action="/adm/grades">');      $r->print('<h3>'.&mt('Select the problem or one of the problems you want to grade').'</h3><form action="/adm/grades">');
     $r->print(&Apache::lonstathelpers::problem_selector('.',undef,1,undef,undef,1));      $r->print(&Apache::lonstathelpers::problem_selector('.',undef,1,undef,undef,1,1));
     $r->print('<input type="hidden" name="command" value="gradingmenu" />');      $r->print('<input type="hidden" name="command" value="gradingmenu" />');
     $r->print('<input type="submit" value="'.&mt('Next').' &rarr;" /></form>');      $r->print('<input type="submit" value="'.&mt('Next').' &rarr;" /></form>');
 }  }
   
 #----- display problem, answer, and submissions for a single student (no grading)  
   
 sub view_as_user {  
     my ($symb,$vuname,$vudom,$hasperm) = @_;  
     my $plainname = &Apache::loncommon::plainname($vuname,$vudom,'lastname');  
     my $displayname = &nameUserString('',$plainname,$vuname,$vudom);  
     my $output = &Apache::loncommon::get_student_view($symb,$vuname,$vudom,  
                                                       $env{'request.course.id'},  
                                                       undef,{'disable_submit' => 1}).  
                  "\n\n".  
                  '<div class="LC_grade_show_user">'.  
                  '<h2>'.$displayname.'</h2>'.  
                  "\n".  
                  &Apache::loncommon::track_student_link('View recent activity',  
                                                         $vuname,$vudom,'check').' '.  
                  "\n";  
     if (&Apache::lonnet::allowed('opa',$env{'request.course.id'}) ||  
         (($env{'request.course.sec'} ne '') &&  
          &Apache::lonnet::allowed('opa',$env{'request.course.id'}.'/'.$env{'request.course.sec'}))) {  
         $output .= &Apache::loncommon::pprmlink(&mt('Set/Change parameters'),  
                                                $vuname,$vudom,$symb,'check');  
     }  
     $output .= "\n";  
     my $companswer = &Apache::loncommon::get_student_answers($symb,$vuname,$vudom,  
                                                              $env{'request.course.id'});  
     $companswer=~s|<form(.*?)>||g;  
     $companswer=~s|</form>||g;  
     $companswer=~s|name="submit"|name="would_have_been_submit"|g;  
     $output .= '<div class="LC_Box">'.  
                '<h3 class="LC_hcell">'.&mt('Correct answer for[_1]',$displayname).'</h3>'.  
                $companswer.  
                '</div>'."\n";  
     my $is_tool = ($symb =~ /ext\.tool$/);  
     my ($essayurl,%coursedesc_by_cid);  
     (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);  
     my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$vudom,$vuname);  
     my $res_error;  
     my ($partlist,$handgrade,$responseType,$numresp,$numessay) =  
         &response_type($symb,\$res_error);  
     my $fullname;  
     my $collabinfo;  
     if ($numessay) {  
         unless ($hasperm) {  
             &init_perm();  
         }  
         ($collabinfo,$fullname)=  
             &check_collaborators($symb,$vuname,$vudom,\%record,$handgrade,0);  
         unless ($hasperm) {  
             &reset_perm();  
         }  
     }  
     my $checkIcon = '<img alt="'.&mt('Check Mark').  
                     '" src="'.$Apache::lonnet::perlvar{'lonIconsURL'}.  
                     '/check.gif" height="16" border="0" />';  
     my ($lastsubonly,$partinfo) =  
         &show_last_submission($vuname,$vudom,$symb,$essayurl,$responseType,'datesub',  
                               '',$fullname,\%record,\%coursedesc_by_cid);  
     $output .= '<div class="LC_Box">'.  
                '<h3 class="LC_hcell">'.&mt('Submissions').'</h3>'."\n".$collabinfo."\n";  
     if (($numresp > $numessay) & !$is_tool) {  
         $output .='<p class="LC_info">'.  
                   &mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon).  
                   "</p>\n";  
     }  
     $output .= $partinfo;  
     $output .= $lastsubonly;  
     $output .= &displaySubByDates($symb,\%record,$partlist,$responseType,$checkIcon,$vuname,$vudom);  
     $output .= '</div></div>'."\n";  
     return $output;  
 }  
   
 sub handler {  sub handler {
     my $request=$_[0];      my $request=$_[0];
     &reset_caches();      &reset_caches();
Line 12450  sub handler { Line 11525  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 11548  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 11561  sub handler {
     if (($symb eq '' || $command eq '') && ($env{'request.course.id'})) {      if (($symb eq '' || $command eq '') && ($env{'request.course.id'})) {
 #  #
 # Not called from a resource, but inside a course  # Not called from a resource, but inside a course
 #  #    
         &startpage($request,undef,[],1,1);          &startpage($request,undef,[],1,1);
         &select_problem($request);          &select_problem($request);
     } else {      } else {
         if ($command eq 'submission' && $perm{'vgr'}) {   if ($command eq 'submission' && $perm{'vgr'}) {
             my ($stuvcurrent,$stuvdisp,$versionform,$js,$onload);              my ($stuvcurrent,$stuvdisp,$versionform,$js,$onload);
             if (($env{'form.student'} ne '') && ($env{'form.userdom'} ne '')) {              if (($env{'form.student'} ne '') && ($env{'form.userdom'} ne '')) {
                 ($stuvcurrent,$stuvdisp,$versionform,$js) =                  ($stuvcurrent,$stuvdisp,$versionform,$js) =
Line 12523  sub handler { Line 11594  sub handler {
                 }                  }
                 $request->print($versionform);                  $request->print($versionform);
             }              }
             ($env{'form.student'} eq '' ? &listStudents($request,$symb,'',$divforres) : &submission($request,0,0,$symb,$divforres,$command));      ($env{'form.student'} eq '' ? &listStudents($request,$symb,'',$divforres) : &submission($request,0,0,$symb,$divforres,$command));
         } elsif ($command eq 'versionsub' && $perm{'vgr'}) {          } elsif ($command eq 'versionsub' && $perm{'vgr'}) {
             my ($stuvcurrent,$stuvdisp,$versionform,$js) =              my ($stuvcurrent,$stuvdisp,$versionform,$js) =
                 &choose_task_version_form($symb,$env{'form.student'},                  &choose_task_version_form($symb,$env{'form.student'},
Line 12543  sub handler { Line 11614  sub handler {
             }              }
             $request->print('<br clear="all" />');              $request->print('<br clear="all" />');
             $request->print(&show_previous_task_version($request,$symb));              $request->print(&show_previous_task_version($request,$symb));
         } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {   } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
                                        {href=>'',text=>'Select student'}],1,1);                                         {href=>'',text=>'Select student'}],1,1);
             &pickStudentPage($request,$symb);      &pickStudentPage($request,$symb);
         } elsif ($command eq 'displayPage' && $perm{'vgr'}) {   } elsif ($command eq 'displayPage' && $perm{'vgr'}) {
             my $csslinks;              my $csslinks;
             unless ($env{'form.vProb'} eq 'no') {              unless ($env{'form.vProb'} eq 'no') {
                 $csslinks = &Apache::loncommon::css_links($symb,'map');                  $csslinks = &Apache::loncommon::css_links($symb,'map');
Line 12556  sub handler { Line 11627  sub handler {
                                       [{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},                                        [{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
                                        {href=>'',text=>'Select student'},                                         {href=>'',text=>'Select student'},
                                        {href=>'',text=>'Grade student'}],1,1,undef,undef,undef,$csslinks);                                         {href=>'',text=>'Grade student'}],1,1,undef,undef,undef,$csslinks);
             &displayPage($request,$symb);      &displayPage($request,$symb);
         } elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {   } elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
                                        {href=>'',text=>'Select student'},                                         {href=>'',text=>'Select student'},
                                        {href=>'',text=>'Grade student'},                                         {href=>'',text=>'Grade student'},
                                        {href=>'',text=>'Store grades'}],1,1);                                         {href=>'',text=>'Store grades'}],1,1);
             &updateGradeByPage($request,$symb);      &updateGradeByPage($request,$symb);
         } elsif ($command eq 'processGroup' && $perm{'vgr'}) {   } elsif ($command eq 'processGroup' && $perm{'vgr'}) {
             my $csslinks;              my $csslinks;
             unless ($env{'form.vProb'} eq 'no') {              unless ($env{'form.vProb'} eq 'no') {
                 $csslinks = &Apache::loncommon::css_links($symb);                  $csslinks = &Apache::loncommon::css_links($symb);
             }              }
             &startpage($request,$symb,[{href=>'',text=>'...'},              &startpage($request,$symb,[{href=>'',text=>'...'},
                                        {href=>'',text=>'Modify grades'}],undef,undef,undef,undef,undef,$csslinks,undef,1);                                         {href=>'',text=>'Modify grades'}],undef,undef,undef,undef,undef,$csslinks,undef,1);
             &processGroup($request,$symb);      &processGroup($request,$symb);
         } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {   } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {
             &startpage($request,$symb);              &startpage($request,$symb);
             $request->print(&grading_menu($request,$symb));      $request->print(&grading_menu($request,$symb));
         } elsif ($command eq 'individual' && $perm{'vgr'}) {   } elsif ($command eq 'individual' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>'',text=>'Select individual students to grade'}]);              &startpage($request,$symb,[{href=>'',text=>'Select individual students to grade'}]);
             $request->print(&submit_options($request,$symb));      $request->print(&submit_options($request,$symb));
         } elsif ($command eq 'ungraded' && $perm{'vgr'}) {          } elsif ($command eq 'ungraded' && $perm{'vgr'}) {
             my $js = &part_selector_js();              my $js = &part_selector_js();
             my $onload = "toggleParts('gradesub');";              my $onload = "toggleParts('gradesub');";
Line 12589  sub handler { Line 11660  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 11692  sub handler {
                                        {href=>'', text=>'Process clicker file'},                                         {href=>'', text=>'Process clicker file'},
                                        {href=>'', text=>'Store grades'}]);                                         {href=>'', text=>'Store grades'}]);
             $request->print(&assign_clicker_grades($request,$symb));              $request->print(&assign_clicker_grades($request,$symb));
         } elsif ($command eq 'csvform' && $perm{'mgr'}) {   } elsif ($command eq 'csvform' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
             $request->print(&upcsvScores_form($request,$symb));      $request->print(&upcsvScores_form($request,$symb));
         } elsif ($command eq 'csvupload' && $perm{'mgr'}) {   } elsif ($command eq 'csvupload' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
             $request->print(&csvupload($request,$symb));      $request->print(&csvupload($request,$symb));
         } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {   } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {
             &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
             $request->print(&csvuploadmap($request,$symb));      $request->print(&csvuploadmap($request,$symb));
         } elsif ($command eq 'csvuploadoptions' && $perm{'mgr'}) {   } elsif ($command eq 'csvuploadoptions' && $perm{'mgr'}) {
             if ($env{'form.associate'} ne 'Reverse Association') {      if ($env{'form.associate'} ne 'Reverse Association') {
                 &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);                  &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
                 $request->print(&csvuploadoptions($request,$symb));   $request->print(&csvuploadoptions($request,$symb));
             } else {      } else {
                 if ( $env{'form.upfile_associate'} ne 'reverse' ) {   if ( $env{'form.upfile_associate'} ne 'reverse' ) {
                     $env{'form.upfile_associate'} = 'reverse';      $env{'form.upfile_associate'} = 'reverse';
                 } else {   } else {
                     $env{'form.upfile_associate'} = 'forward';      $env{'form.upfile_associate'} = 'forward';
                 }   }
                 &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);                  &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
                 $request->print(&csvuploadmap($request,$symb));   $request->print(&csvuploadmap($request,$symb));
             }      }
         } elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) {   } elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) {
             &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
             $request->print(&csvuploadassign($request,$symb));      $request->print(&csvuploadassign($request,$symb));
         } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {   } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,
                        undef,undef,undef,undef,'toggleScantab(document.rules);');                         undef,undef,undef,undef,'toggleScantab(document.rules);');
             $request->print(&scantron_selectphase($request,undef,$symb));      $request->print(&scantron_selectphase($request,undef,$symb));
         } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {    } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&scantron_do_warning($request,$symb));       $request->print(&scantron_do_warning($request,$symb));
         } elsif ($command eq 'scantron_validate' && $perm{'mgr'}) {   } elsif ($command eq 'scantron_validate' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&scantron_validate_file($request,$symb));      $request->print(&scantron_validate_file($request,$symb));
         } elsif ($command eq 'scantron_process' && $perm{'mgr'}) {   } elsif ($command eq 'scantron_process' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&scantron_process_students($request,$symb));      $request->print(&scantron_process_students($request,$symb));
         } elsif ($command eq 'scantronupload' &&    } elsif ($command eq 'scantronupload' && 
                  (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||    (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
                   &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {  
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,
                        undef,undef,undef,undef,'toggleScantab(document.rules);');                         undef,undef,undef,undef,'toggleScantab(document.rules);');
             $request->print(&scantron_upload_scantron_data($request,$symb));       $request->print(&scantron_upload_scantron_data($request,$symb)); 
         } elsif ($command eq 'scantronupload_save' &&    } elsif ($command eq 'scantronupload_save' &&
                  (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||    (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
                   &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
        $request->print(&scantron_upload_scantron_data_save($request,$symb));
     } elsif ($command eq 'scantron_download' && ($perm{'usc'} || $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_upload_scantron_data_save($request,$symb));       $request->print(&scantron_download_scantron_data($request,$symb));
         } elsif ($command eq 'scantron_download' &&          } elsif ($command eq 'scantronupload_delete' &&
                  &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {                   (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
             &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));              &scantron_upload_delete($request,$symb);
         } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {          } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&checkscantron_results($request,$symb));              $request->print(&checkscantron_results($request,$symb));
Line 12690  sub handler { Line 11762  sub handler {
                undef,undef,undef,undef,undef,undef,undef,1);                 undef,undef,undef,undef,undef,undef,undef,1);
             $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');              $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');
             &submit_download_link($request,$symb);              &submit_download_link($request,$symb);
         } elsif ($command 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());
       } elsif ($env{'request.course.id'}) {
           &Apache::lonquickgrades::endGradeScreen($request);
       }
     &reset_caches();      &reset_caches();
     return OK;      return OK;
 }  }
Line 12760  described at http://www.lon-capa.org. Line 11798  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 11845  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 12843  ssi_with_retries() Line 11950  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 12902  ssi_with_retries() Line 12010  ssi_with_retries()
 =item  scantron_upload_scantron_data_save() :   =item  scantron_upload_scantron_data_save() : 
   
    Adds a provided bubble information data file to the course if user     Adds a provided bubble information data file to the course if user
    has the correct privileges to do so.      has the correct privileges to do so.
   
   = item scantron_upload_delete() :
   
      Deletes a previously uploaded bubble information data file, if user
      was the one who uploaded the file, and has the privileges to do so.
   
 =item  valid_file() :  =item  valid_file() :
   
Line 12922  ssi_with_retries() Line 12035  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.10  
changed lines
  Added in v.1.788


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