--- loncom/homework/grades.pm 2011/12/01 00:36:59 1.596.2.12 +++ loncom/homework/grades.pm 2013/05/10 23:31:37 1.596.2.12.2.13 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.596.2.12 2011/12/01 00:36:59 raeburn Exp $ +# $Id: grades.pm,v 1.596.2.12.2.13 2013/05/10 23:31:37 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -52,6 +52,7 @@ use POSIX qw(floor); my %perm=(); +my %old_essays=(); # These variables are used to recover from ssi errors @@ -124,13 +125,16 @@ sub getpartlist { # --- Get the symbolic name of a problem and the url sub get_symb { my ($request,$silent) = @_; - (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--; - my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url))); - if ($symb eq '') { - if (!$silent) { - $request->print(&mt("Unable to handle ambiguous references: [_1].",$url)); - return (); - } + my $symb=$env{'form.symb'}; + unless ($symb) { + (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--; + $symb = &Apache::lonnet::symbread($url); + if ($symb eq '') { + if (!$silent) { + $request->print(&mt("Unable to handle ambiguous references: [_1].",$url)); + return (); + } + } } &Apache::lonenc::check_decrypt(\$symb); return ($symb); @@ -249,7 +253,7 @@ sub showResourceInfo { $result.=''.$display_part.'' .''.''.$resID.'' .''.&mt($responsetype).'' -# .''.&mt('Handgrade: [_1]',$handgrade).'' +# .''.&mt('Handgrade: [_1]',$handgrade).'' .&Apache::loncommon::end_data_table_row(); } } @@ -260,6 +264,7 @@ sub showResourceInfo { sub reset_caches { &reset_analyze_cache(); &reset_perm(); + &reset_old_essays(); } { @@ -272,7 +277,7 @@ sub reset_caches { } sub get_analyze { - my ($symb,$uname,$udom,$no_increment,$add_to_hash,$type,$trial,$rndseed)=@_; + my ($symb,$uname,$udom,$no_increment,$add_to_hash,$type,$trial,$rndseed,$bubbles_per_row)=@_; my $key = "$symb\0$uname\0$udom"; if ($type eq 'randomizetry') { if ($trial ne '') { @@ -306,6 +311,9 @@ sub reset_caches { 'grade_courseid' => $env{'request.course.id'}, 'grade_username' => $uname, 'grade_noincrement' => $no_increment); + if ($bubbles_per_row ne '') { + $form{'bubbles_per_row'} = $bubbles_per_row; + } if ($type eq 'randomizetry') { $form{'grade_questiontype'} = $type; if ($rndseed ne '') { @@ -346,7 +354,7 @@ sub reset_caches { } sub scantron_partids_tograde { - my ($resource,$cid,$uname,$udom,$check_for_randomlist) = @_; + my ($resource,$cid,$uname,$udom,$check_for_randomlist,$bubbles_per_row) = @_; my (%analysis,@parts); if (ref($resource)) { my $symb = $resource->symb(); @@ -354,7 +362,9 @@ sub reset_caches { if ($check_for_randomlist) { $add_to_form = { 'check_parts_withrandomlist' => 1,}; } - my $analyze = &get_analyze($symb,$uname,$udom,undef,$add_to_form); + my $analyze = + &get_analyze($symb,$uname,$udom,undef,$add_to_form, + undef,undef,undef,$bubbles_per_row); if (ref($analyze) eq 'HASH') { %analysis = %{$analyze}; } @@ -738,7 +748,11 @@ sub compute_points { # sub most_similar { - my ($uname,$udom,$uessay,$old_essays)=@_; + my ($uname,$udom,$symb,$uessay)=@_; + + unless ($symb) { return ''; } + + unless (ref($old_essays{$symb}) eq 'HASH') { return ''; } # ignore spaces and punctuation @@ -755,11 +769,11 @@ sub most_similar { my $scrsid=''; my $sessay=''; # go through all essays ... - foreach my $tkey (keys(%$old_essays)) { + foreach my $tkey (keys(%{$old_essays{$symb}})) { my ($tname,$tdom,$tcrsid)=map {&unescape($_)} (split(/\./,$tkey)); # ... except the same student next if (($tname eq $uname) && ($tdom eq $udom)); - my $tessay=$old_essays->{$tkey}; + my $tessay=$old_essays{$symb}{$tkey}; $tessay=~s/\W+/ /gs; # String similarity gives up if not even limit my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit); @@ -769,7 +783,7 @@ sub most_similar { $sname=$tname; $sdom=$tdom; $scrsid=$tcrsid; - $sessay=$old_essays->{$tkey}; + $sessay=$old_essays{$symb}{$tkey}; } } if ($limit>0.6) { @@ -1605,17 +1619,15 @@ INNERJS function savedMsgHeader(Nmsg,usrctr,fullname) { var height = 70*Nmsg+250; - var scrollbar = "no"; if (height > 600) { height = 600; - scrollbar = "yes"; } var xpos = (screen.width-600)/2; xpos = (xpos < 0) ? '0' : xpos; var ypos = (screen.height-height)/2-30; ypos = (ypos < 0) ? '0' : ypos; - pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=700,height='+height); + pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars=yes,screenx='+xpos+',screeny='+ypos+',width=700,height='+height); pWin.focus(); pDoc = pWin.document; pDoc.$docopen; @@ -1825,7 +1837,6 @@ sub gradeBox { $line.=''."\n"; - #&mt('Part:[_1]Points:[_2]or[_3]',$display_part,$radio,$line); $result .= ''.$display_part.''.$radio.''.&mt('or').''.$line.''; $result.=&Apache::loncommon::end_data_table_row(); @@ -1912,14 +1923,27 @@ sub show_problem { $companswer=~s|||g; $companswer=~s|name="submit"|name="would_have_been_submit"|g; } + my $renderheading = &mt('View of the problem'); + my $answerheading = &mt('Correct answer'); + if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) { + my $stu_fullname = $env{'form.fullname'}; + if ($stu_fullname eq '') { + $stu_fullname = &Apache::loncommon::plainname($uname,$udom,'lastname'); + } + my $forwhom = &nameUserString(undef,$stu_fullname,$uname,$udom); + if ($forwhom ne '') { + $renderheading = &mt('View of the problem for[_1]',$forwhom); + $answerheading = &mt('Correct answer for[_1]',$forwhom); + } + } $rendered= '
' - .'

'.&mt('View of the problem').'

' + .'

'.$renderheading.'

' .$rendered .'
'; $companswer= '
' - .'

'.&mt('Correct answer').'

' + .'

'.$answerheading.'

' .$companswer .'
'; my $result; @@ -1990,7 +2014,7 @@ sub submission { $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student? my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'}); $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq ''; - my $symb = &get_symb($request); + my ($symb) = &get_symb($request); if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; } if (!&canview($usec)) { @@ -2009,7 +2033,6 @@ sub submission { '" src="'.$request->dir_config('lonIconsURL'). '/check.gif" height="16" border="0" />'; - my %old_essays; # header info if ($counter == 0) { &sub_page_js($request); @@ -2125,7 +2148,7 @@ KEYWORDS my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/); $apath=&escape($apath); $apath=~s/\W/\_/gs; - %old_essays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname); + &init_old_essays($symb,$apath,$adom,$aname); } } @@ -2262,7 +2285,7 @@ KEYWORDS } if($env{'form.checkPlag'}){ my ($oname,$odom,$ocrsid,$oessay,$osim)= - &most_similar($uname,$udom,$subval,\%old_essays); + &most_similar($uname,$udom,$symb,$subval); if ($osim) { $osim=int($osim*100.0); my %old_course_desc = @@ -2594,10 +2617,187 @@ sub keywords_highlight { return $string; } +# For Tasks provide a mechanism to display previous version for one specific student + +sub show_previous_task_version { + my ($request,$symb) = @_; + if ($symb eq '') { + $request->print("Unable to handle ambiguous references."); + + return ''; + } + my ($uname,$udom) = ($env{'form.student'},$env{'form.userdom'}); + my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'}); + if (!&canview($usec)) { + $request->print('Unable to view previous version for requested student.('. + $uname.':'.$udom.' in section '.$usec.' in course id '. + $env{'request.course.id'}.')'); + return; + } + my $mode = 'both'; + my $isTask = ($symb =~/\.task$/); + if ($isTask) { + if ($env{'form.previousversion'} =~ /^\d+$/) { + if ($env{'form.fullname'} eq '') { + $env{'form.fullname'} = + &Apache::loncommon::plainname($uname,$udom,'lastname'); + } + my $probtitle=&Apache::lonnet::gettitle($symb); + $request->print("\n\n". + '
'. + '

'.&nameUserString(undef,$env{'form.fullname'},$uname,$udom). + '

'."\n"); + &Apache::lonxml::clear_problem_counter(); + $request->print(&show_problem($request,$symb,$uname,$udom,1,1,$mode, + {'previousversion' => $env{'form.previousversion'} })); + $request->print("\n
"); + } + } + return; +} + +sub choose_task_version_form { + my ($symb,$uname,$udom,$nomenu) = @_; + my $isTask = ($symb =~/\.task$/); + my ($current,$version,$result,$js,$displayed,$rowtitle); + if ($isTask) { + my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'}, + $udom,$uname); + if (($record{'resource.0.version'} eq '') || + ($record{'resource.0.version'} < 2)) { + return ($record{'resource.0.version'}, + $record{'resource.0.version'},$result,$js); + } else { + $current = $record{'resource.0.version'}; + } + if ($env{'form.previousversion'}) { + $displayed = $env{'form.previousversion'}; + $rowtitle = &mt('Choose another version:') + } else { + $displayed = $current; + $rowtitle = &mt('Show earlier version:'); + } + $result = '
'; + my $list; + my $numversions = 0; + for (my $i=1; $i<=$record{'resource.0.version'}; $i++) { + if ($i == $current) { + if (!$env{'form.previousversion'} || $nomenu) { + next; + } else { + $list .= ''."\n"; + $numversions ++; + } + } elsif (defined($record{'resource.'.$i.'.0.status'})) { + unless ($i == $env{'form.previousversion'}) { + $numversions ++; + } + $list .= ''."\n"; + } + } + if ($numversions) { + $symb = &HTML::Entities::encode($symb,'<>"&'); + $result .= + '
'. + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_row(). + ''.$rowtitle.''. + ''. + &Apache::loncommon::end_data_table_row(); + unless ($nomenu) { + $result .= &Apache::loncommon::start_data_table_row(). + ''.&mt('Open in new window').''. + ''. + ''. + ''. + ''. + &Apache::loncommon::end_data_table_row(); + } + $result .= + &Apache::loncommon::start_data_table_row(). + ' '. + ''. + ''. + ''. + &Apache::loncommon::end_data_table_row(). + &Apache::loncommon::end_data_table(). + '
'; + $js = &previous_display_javascript($nomenu,$current); + } elsif ($displayed && $nomenu) { + $result .= ''.&mt('Close window').''; + } else { + $result .= &mt('No previous versions to show for this student'); + } + $result .= '
'; + } + return ($current,$displayed,$result,$js); +} + +sub previous_display_javascript { + my ($nomenu,$current) = @_; + my $js = <<"JSONE"; + +ENDJS + +} + #--- Called from submission routine sub processHandGrade { my ($request) = shift; - my $symb = &get_symb($request); + my ($symb) = &get_symb($request); my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb); my $button = $env{'form.gradeOpt'}; my $ngrade = $env{'form.NCT'}; @@ -3002,9 +3202,11 @@ sub handback_files { &file_name_version_ext($answer_file); my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/); my $getpropath = 1; - my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$portfolio_path,$domain,$stuname,$getpropath); - my $version = &get_next_version($answer_name, $answer_ext, \@dir_list); - # fix file name + my ($dir_list,$listerror) = + &Apache::lonnet::dirlist($portfolio_root.$portfolio_path, + $domain,$stuname,$getpropath); + my $version = &get_next_version($answer_name,$answer_ext,$dir_list); + # fix filename my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/); my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain, $newflg.'_'.$part_resp.'_returndoc'.$counter, @@ -3024,7 +3226,7 @@ sub handback_files { $file_msg.=''.$save_file_name."
"; } - $request->print('
'.&mt('[_1] will be the uploaded file name [_2]',''.$fname.'',''.$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter}.'')); + $request->print('
'.&mt('[_1] will be the uploaded filename [_2]',''.$fname.'',''.$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter}.'')); } } } @@ -3163,9 +3365,11 @@ sub version_portfiles { my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/); my ($answer_name,$answer_ver,$answer_ext) = &file_name_version_ext($answer_file); - my $getpropath = 1; - my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$directory,$domain,$stu_name,$getpropath); - my $version = &get_next_version($answer_name, $answer_ext, \@dir_list); + 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); @@ -3184,21 +3388,24 @@ sub version_portfiles { sub get_next_version { my ($answer_name, $answer_ext, $dir_list) = @_; my $version; - 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 (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; + # a versioned file is found so save it for later + if ($file_version > $version) { + $version = $file_version; + } } } } - } + } $version ++; return($version); } @@ -3658,7 +3865,7 @@ sub viewstudentgrade { sub editgrades { my ($request) = @_; - my $symb=&get_symb($request); + my ($symb)=&get_symb($request); my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section')); my $title='

'.&mt('Current Grade Status').'

'; $title.='

'.&mt('Current Resource: [_1]',$env{'form.probTitle'}).'

'."\n"; @@ -4603,8 +4810,8 @@ sub displayPage { &Apache::loncommon::start_data_table_row(). ''.$prob. (scalar(@{$parts}) == 1 ? '' - : '
('.&mt('[_1]parts)', - scalar(@{$parts}).' ') + : '
('.&mt('[_1]parts', + scalar(@{$parts}).' ').')' ). ''; $studentTable.=''; @@ -4686,6 +4893,7 @@ sub displaySubByDates { &Apache::loncommon::start_data_table_header_row(). ''.&mt('Date/Time').''. ($isCODE?''.&mt('CODE').'':''). + ($isTask?''.&mt('Version').'':''). ''.&mt('Submission').''. ''.&mt('Status').''. &Apache::loncommon::end_data_table_header_row(); @@ -4706,7 +4914,9 @@ sub displaySubByDates { if (exists($$record{$version.':resource.0.version'})) { $interaction = $$record{$version.':resource.0.version'}; } - + if ($isTask && $env{'form.previousversion'}) { + next unless ($interaction == $env{'form.previousversion'}); + } my $where = ($isTask ? "$version:resource.$interaction" : "$version:resource"); $studentTable.=&Apache::loncommon::start_data_table_row(). @@ -4714,6 +4924,9 @@ sub displaySubByDates { if ($isCODE) { $studentTable.=''.$record->{$version.':resource.CODE'}.''; } + if ($isTask) { + $studentTable.=''.$interaction.''; + } my @versionKeys = split(/\:/,$$record{$version.':keys'}); my @displaySub = (); foreach my $partid (@{$parts}) { @@ -4733,7 +4946,7 @@ sub displaySubByDates { my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/) : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/)); - $displaySub[0].='' .' ' .'('.&mt('Response ID: [_1]',$responseId).')' @@ -5157,14 +5370,16 @@ sub scantron_filenames { my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'}; my $cname=$env{'course.'.$env{'request.course.id'}.'.num'}; my $getpropath = 1; - my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname, - $getpropath); + my ($dirlist,$listerror) = &Apache::lonnet::dirlist('userfiles',$cdom, + $cname,$getpropath); my @possiblenames; - foreach my $filename (sort(@files)) { - ($filename)=split(/&/,$filename); - if ($filename!~/^scantron_orig_/) { next ; } - $filename=~s/^scantron_orig_//; - push(@possiblenames,$filename); + if (ref($dirlist) eq 'ARRAY') { + foreach my $filename (sort(@{$dirlist})) { + ($filename)=split(/&/,$filename); + if ($filename!~/^scantron_orig_/) { next ; } + $filename=~s/^scantron_orig_//; + push(@possiblenames,$filename); + } } return @possiblenames; } @@ -5562,6 +5777,8 @@ sub scantron_selectphase { LastName - column that the last name starts in LastNameLength - number of columns that the last name spans + BubblesPerRow - number of bubbles available in each row used to + bubble an answer. (If not specified, 10 assumed). =cut @@ -5592,6 +5809,7 @@ sub get_scantron_config { $config{'FirstNamelength'}=$config[14]; $config{'LastName'}=$config[15]; $config{'LastNamelength'}=$config[16]; + $config{'BubblesPerRow'}=$config[17]; last; } return %config; @@ -6376,6 +6594,12 @@ sub scantron_warning_screen { ''.&mt('List of CODES to validate against:').''. $env{'form.scantron_CODElist'}.''; } + my $lastbubblepoints; + if ($env{'form.scantron_lastbubblepoints'} ne '') { + $lastbubblepoints = + ''.&mt('Hand-graded items: points from last bubble in row').''. + $env{'form.scantron_lastbubblepoints'}.''; + } return ('

@@ -6384,11 +6608,11 @@ sub scantron_warning_screen { -'.$CODElist.' +'.$CODElist.$lastbubblepoints.'
'.&mt('Sequence to be Graded:').''.$title.'
'.&mt('Data File that will be used:').''.$env{'form.scantron_selectfile'}.'

-

'.&mt('If this information is correct, please click on \'[_1]\'.',&mt($button_text)).'

-

'.&mt('If something is incorrect, please click the \'Grading Menu\' button to start over.').'

+

'.&mt("If this information is correct, please click on '[_1]'.",&mt($button_text)).'

+

'.&mt("If something is incorrect, please click the 'Grading Menu' button to start over.").'


'); @@ -6424,8 +6648,9 @@ sub scantron_do_warning { } } else { my $warning=&scantron_warning_screen('Grading: Validate Records'); + my $bubbledbyhand=&hand_bubble_option(); $r->print(' -'.$warning.' +'.$warning.$bubbledbyhand.' '); @@ -6515,12 +6740,16 @@ sub scantron_validate_file { #get the student pick code ready $r->print(&Apache::loncommon::studentbrowser_javascript()); my $nav_error; - my $max_bubble=&scantron_get_maxbubble(\$nav_error); + my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); + my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config); if ($nav_error) { $r->print(&navmap_errormsg()); return ''; } my $result=&scantron_form_start($max_bubble).$default_form_data; + if ($env{'form.scantron_lastbubblepoints'} ne '') { + $result .= ''; + } $r->print($result); my @validate_phases=( 'sequence', @@ -6946,7 +7175,12 @@ sub scantron_validate_sequence { my @resources= $navmap->retrieveResources($map,\&scantron_filter_not_exam,1,0); if (@resources) { - $r->print("

".&mt('Some resources in the sequence currently are not set to exam mode. Grading these resources currently may not work correctly.')."

"); + $r->print('

' + .&mt('Some resources in the sequence currently are not set to' + .' exam mode. Grading these resources currently may not' + .' work correctly.') + .'

' + ); return (1,$currentphase); } } @@ -6968,7 +7202,7 @@ sub scantron_validate_ID { my ($scanlines,$scan_data)=&scantron_getfile(); my $nav_error; - &scantron_get_maxbubble(\$nav_error); # parse needs the bubble_lines.. array. + &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the bubble_lines.. array. if ($nav_error) { $r->print(&navmap_errormsg()); return(1,$currentphase); @@ -7077,7 +7311,7 @@ sub scantron_get_correction { $r->print(&Apache::loncommon::selectstudent_link('scantronupload', 'scantron_username','scantron_domain')); $r->print(": "); - $r->print("\n@". + $r->print("\n:\n". &Apache::loncommon::select_dom_form($env{'request.role.domain'},'scantron_domain')); $r->print(''); @@ -7381,7 +7615,19 @@ sub scantron_bubble_selector { my $max=$$scan_config{'Qlength'}; my $scmode=$$scan_config{'Qon'}; - if ($scmode eq 'number' || $scmode eq 'letter') { $max=10; } + if ($scmode eq 'number' || $scmode eq 'letter') { + if (($$scan_config{'BubblesPerRow'} =~ /^\d+$/) && + ($$scan_config{'BubblesPerRow'} > 0)) { + $max=$$scan_config{'BubblesPerRow'}; + if (($scmode eq 'number') && ($max > 10)) { + $max = 10; + } elsif (($scmode eq 'letter') && $max > 26) { + $max = 26; + } + } else { + $max = 10; + } + } my @alphabet=('A'..'Z'); $r->print(&Apache::loncommon::start_data_table(). @@ -7536,7 +7782,7 @@ sub scantron_validate_CODE { my %allcodes=&get_codes(); my $nav_error; - &scantron_get_maxbubble(\$nav_error); # parse needs the lines per response array. + &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the lines per response array. if ($nav_error) { $r->print(&navmap_errormsg()); return(1,$currentphase); @@ -7595,7 +7841,7 @@ sub scantron_validate_doublebubble { my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); my ($scanlines,$scan_data)=&scantron_getfile(); my $nav_error; - &scantron_get_maxbubble(\$nav_error); # parse needs the bubble line array. + &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the bubble line array. if ($nav_error) { $r->print(&navmap_errormsg()); return(1,$currentphase); @@ -7617,7 +7863,7 @@ sub scantron_validate_doublebubble { sub scantron_get_maxbubble { - my ($nav_error) = @_; + my ($nav_error,$scantron_config) = @_; if (defined($env{'form.scantron_maxbubble'}) && $env{'form.scantron_maxbubble'}) { &restore_bubble_lines(); @@ -7636,6 +7882,7 @@ sub scantron_get_maxbubble { } my $map=$navmap->getResourceByUrl($sequence); my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); + my $bubbles_per_row = &bubblesheet_bubbles_per_row($scantron_config); &Apache::lonxml::clear_problem_counter(); @@ -7651,7 +7898,8 @@ sub scantron_get_maxbubble { my $response_number = 0; my $bubble_line = 0; foreach my $resource (@resources) { - my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,$udom); + my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname, + $udom,$bubbles_per_row); if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) { foreach my $part_id (@{$parts}) { my $lines; @@ -7680,9 +7928,10 @@ sub scantron_get_maxbubble { if (ref($analysis->{$part_id.'.shown'}) eq 'ARRAY') { $numshown = scalar(@{$analysis->{$part_id.'.shown'}}); } - my $bubbles_per_line = 10; - my $inner_bubble_lines = int($numbub/$bubbles_per_line); - if (($numbub % $bubbles_per_line) != 0) { + my $bubbles_per_row = + &bubblesheet_bubbles_per_row($scantron_config); + my $inner_bubble_lines = int($numbub/$bubbles_per_row); + if (($numbub % $bubbles_per_row) != 0) { $inner_bubble_lines++; } for (my $i=0; $i<$numshown; $i++) { @@ -7693,7 +7942,7 @@ sub scantron_get_maxbubble { $lines = $numshown * $inner_bubble_lines; } else { $lines = $analysis->{"$part_id.bubble_lines"}; - } + } $first_bubble_line{$response_number} = $bubble_line; $bubble_lines_per_response{$response_number} = $lines; @@ -7714,6 +7963,18 @@ sub scantron_get_maxbubble { return $env{'form.scantron_maxbubble'}; } +sub bubblesheet_bubbles_per_row { + my ($scantron_config) = @_; + my $bubbles_per_row; + if (ref($scantron_config) eq 'HASH') { + $bubbles_per_row = $scantron_config->{'BubblesPerRow'}; + } + if ((!$bubbles_per_row) || ($bubbles_per_row < 1)) { + $bubbles_per_row = 10; + } + return $bubbles_per_row; +} + sub scantron_validate_missingbubbles { my ($r,$currentphase) = @_; #get student info @@ -7724,7 +7985,7 @@ sub scantron_validate_missingbubbles { my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); my ($scanlines,$scan_data)=&scantron_getfile(); my $nav_error; - my $max_bubble=&scantron_get_maxbubble(\$nav_error); + my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config); if ($nav_error) { return(1,$currentphase); } @@ -7771,6 +8032,41 @@ sub scantron_validate_missingbubbles { return (0,$currentphase+1); } +sub hand_bubble_option { + my (undef, undef, $sequence) = + &Apache::lonnet::decode_symb($env{'form.selectpage'}); + return if ($sequence eq ''); + my $navmap = Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + return; + } + my $needs_hand_bubbles; + my $map=$navmap->getResourceByUrl($sequence); + my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); + foreach my $res (@resources) { + if (ref($res)) { + if ($res->is_problem()) { + my $partlist = $res->parts(); + foreach my $part (@{ $partlist }) { + my @types = $res->responseType($part); + if (grep(/^(chem|essay|image|formula|math|string|functionplot)$/,@types)) { + $needs_hand_bubbles = 1; + last; + } + } + } + } + } + if ($needs_hand_bubbles) { + my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); + my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config); + return &mt('The sequence to be graded contains response types which are handgraded.').'

'. + &mt('If you have already graded these by bubbling sheets to indicate points awarded, [_1]what point value is assigned to a filled last bubble in each row?','
'). + ' '.&mt('or').' '. + '

'; + } + return; +} sub scantron_process_students { my ($r) = @_; @@ -7783,6 +8079,8 @@ sub scantron_process_students { my $default_form_data=&defaultFormData($symb); my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); + my $bubbles_per_row = + &bubblesheet_bubbles_per_row(\%scantron_config); my ($scanlines,$scan_data)=&scantron_getfile(); my $classlist=&Apache::loncoursedata::get_classlist(); my %idmap=&username_to_idmap($classlist); @@ -7792,22 +8090,29 @@ sub scantron_process_students { return ''; } my $map=$navmap->getResourceByUrl($sequence); + my $randomorder; + if (ref($map)) { + $randomorder = $map->randomorder(); + } my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); - my (%grader_partids_by_symb,%grader_randomlists_by_symb); + my (%grader_partids_by_symb,%grader_randomlists_by_symb,%ordered); &graders_resources_pass(\@resources,\%grader_partids_by_symb, - \%grader_randomlists_by_symb); - my $resource_error; + \%grader_randomlists_by_symb,$bubbles_per_row); + my ($resource_error,%symb_to_resource,@master_seq); foreach my $resource (@resources) { my $ressymb; if (ref($resource)) { $ressymb = $resource->symb(); + push(@master_seq,$ressymb); + $symb_to_resource{$ressymb} = $resource; } else { $resource_error = 1; last; } my ($analysis,$parts) = &scantron_partids_tograde($resource,$env{'request.course.id'}, - $env{'user.name'},$env{'user.domain'},1); + $env{'user.name'},$env{'user.domain'}, + 1,$bubbles_per_row); $grader_partids_by_symb{$ressymb} = $parts; if (ref($analysis) eq 'HASH') { if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') { @@ -7834,9 +8139,7 @@ SCANTRONFORM my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam')); my $count=&get_todo_count($scanlines,$scan_data); - my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Bubblesheet Status', - 'Bubblesheet Progress',$count, - 'inline',undef,'scantronupload'); + my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count); &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state, 'Processing first student'); $r->print('
'); @@ -7845,7 +8148,7 @@ SCANTRONFORM my $started; my $nav_error; - &scantron_get_maxbubble(\$nav_error); # Need the bubble lines array to parse. + &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse. if ($nav_error) { $r->print(&navmap_errormsg()); return ''; @@ -7888,10 +8191,26 @@ SCANTRONFORM 'Student '.$uname.' has multiple sheets',2); next; } + my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION]; + my $user = $uname.':'.$usec; ($uname,$udom)=split(/:/,$uname); + my $scancode; + if ((exists($scan_record->{'scantron.CODE'})) && + (&Apache::lonnet::validCODE($scan_record->{'scantron.CODE'}))) { + $scancode = $scan_record->{'scantron.CODE'}; + } else { + $scancode = ''; + } + + my @mapresources = @resources; + if ($randomorder) { + @mapresources = + &users_order($user,$scancode,$sequence,\@master_seq,\%ordered, + \%symb_to_resource); + } my (%partids_by_symb,$res_error); - foreach my $resource (@resources) { + foreach my $resource (@mapresources) { my $ressymb; if (ref($resource)) { $ressymb = $resource->symb(); @@ -7902,7 +8221,8 @@ SCANTRONFORM if ((exists($grader_randomlists_by_symb{$ressymb})) || (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) { my ($analysis,$parts) = - &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom); + &scantron_partids_tograde($resource,$env{'request.course.id'}, + $uname,$udom,undef,$bubbles_per_row); $partids_by_symb{$ressymb} = $parts; } else { $partids_by_symb{$ressymb} = $grader_partids_by_symb{$ressymb}; @@ -7922,16 +8242,9 @@ SCANTRONFORM &scantron_putfile($scanlines,$scan_data); } - my $scancode; - if ((exists($scan_record->{'scantron.CODE'})) && - (&Apache::lonnet::validCODE($scan_record->{'scantron.CODE'}))) { - $scancode = $scan_record->{'scantron.CODE'}; - } else { - $scancode = ''; - } - if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode, - \@resources,\%partids_by_symb) eq 'ssi_error') { + \@mapresources,\%partids_by_symb, + $bubbles_per_row) eq 'ssi_error') { $ssi_error = 0; # So end of handler error message does not trigger. $r->print(""); &ssi_print_error($r); @@ -7948,7 +8261,7 @@ SCANTRONFORM $studentdata =~ s/\r$//; my $studentrecord = ''; my $counter = -1; - foreach my $resource (@resources) { + foreach my $resource (@mapresources) { my $ressymb = $resource->symb(); ($counter,my $recording) = &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'}, @@ -7959,7 +8272,8 @@ SCANTRONFORM if ($studentrecord ne $studentdata) { &Apache::lonxml::clear_problem_counter(); if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode, - \@resources,\%partids_by_symb) eq 'ssi_error') { + \@mapresources,\%partids_by_symb, + $bubbles_per_row) eq 'ssi_error') { $ssi_error = 0; # So end of handler error message does not trigger. $r->print(""); &ssi_print_error($r); @@ -7970,7 +8284,7 @@ SCANTRONFORM } $counter = -1; $studentrecord = ''; - foreach my $resource (@resources) { + foreach my $resource (@mapresources) { my $ressymb = $resource->symb(); ($counter,my $recording) = &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'}, @@ -8024,14 +8338,16 @@ SCANTRONFORM } sub graders_resources_pass { - my ($resources,$grader_partids_by_symb,$grader_randomlists_by_symb) = @_; + my ($resources,$grader_partids_by_symb,$grader_randomlists_by_symb, + $bubbles_per_row) = @_; if ((ref($resources) eq 'ARRAY') && (ref($grader_partids_by_symb)) && (ref($grader_randomlists_by_symb) eq 'HASH')) { foreach my $resource (@{$resources}) { my $ressymb = $resource->symb(); my ($analysis,$parts) = &scantron_partids_tograde($resource,$env{'request.course.id'}, - $env{'user.name'},$env{'user.domain'},1); + $env{'user.name'},$env{'user.domain'}, + 1,$bubbles_per_row); $grader_partids_by_symb->{$ressymb} = $parts; if (ref($analysis) eq 'HASH') { if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') { @@ -8044,8 +8360,55 @@ sub graders_resources_pass { return; } +=pod + +=item users_order + + 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 + "NAMEd" exam. + + Should be used when randomorder applied when the corresponding exam was + printed, prior to students completing bubblesheets for the version of the + exam the student received. + +=cut + +sub users_order { + my ($user,$scancode,$mapurl,$master_seq,$ordered,$symb_to_resource) = @_; + my @mapresources; + unless ((ref($ordered) eq 'HASH') && (ref($symb_to_resource) eq 'HASH')) { + return @mapresources; + } + if (($scancode) && (ref($ordered->{$scancode}) eq 'ARRAY')) { + @mapresources = @{$ordered->{$scancode}}; + } elsif ($scancode) { + $env{'form.CODE'} = $scancode; + my $actual_seq = + &Apache::lonprintout::master_seq_to_person_seq($mapurl, + $master_seq, + $user,$scancode); + if (ref($actual_seq) eq 'ARRAY') { + @{$ordered->{$scancode}} = + map { $symb_to_resource->{$_}; } @{$actual_seq}; + @mapresources = @{$ordered->{$scancode}}; + } + delete($env{'form.CODE'}); + } else { + my $actual_seq = + &Apache::lonprintout::master_seq_to_person_seq($mapurl, + $master_seq, + $user); + if (ref($actual_seq) eq 'ARRAY') { + @mapresources = + map { $symb_to_resource->{$_}; } @{$actual_seq}; + } + } + return @mapresources; +} + sub grade_student_bubbles { - my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts) = @_; + my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts,$bubbles_per_row) = @_; if (ref($resources) eq 'ARRAY') { my $count = 0; foreach my $resource (@{$resources}) { @@ -8058,6 +8421,12 @@ sub grade_student_bubbles { 'grade_symb' => $ressymb, 'CODE' => $scancode ); + if ($bubbles_per_row ne '') { + $form{'bubbles_per_row'} = $bubbles_per_row; + } + if ($env{'form.scantron_lastbubblepoints'} ne '') { + $form{'scantron_lastbubblepoints'} = $env{'form.scantron_lastbubblepoints'}; + } if (ref($parts) eq 'HASH') { if (ref($parts->{$ressymb}) eq 'ARRAY') { foreach my $part (@{$parts->{$ressymb}}) { @@ -8084,8 +8453,9 @@ sub scantron_upload_scantron_data { 'domainid', 'coursename',$dom); my $syllabuslink = ''.&mt('Syllabus').''. - (' 'x2).&mt('(shows course personnel)'); - my $default_form_data=&defaultFormData(&get_symb($r,1)); + (' 'x2).&mt('(shows course personnel)'); + my ($symb) = &get_symb($r,1); + my $default_form_data=&defaultFormData($symb); my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.'); my $nocourseid_alert = &mt("Please use the 'Select Course' link to open a separate window where you can search for a course to which a file can be uploaded."); $r->print(' @@ -8290,17 +8660,18 @@ sub valid_file { sub scantron_download_scantron_data { my ($r)=@_; - my $default_form_data=&defaultFormData(&get_symb($r,1)); + my ($symb) = &get_symb($r,1); + my $default_form_data=&defaultFormData($symb); my $cname=$env{'course.'.$env{'request.course.id'}.'.num'}; my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'}; my $file=$env{'form.scantron_selectfile'}; if (! &valid_file($file)) { $r->print('

- '.&mt('The requested file name was invalid.').' + '.&mt('The requested filename was invalid.').'

'); - $r->print(&show_grading_menu_form(&get_symb($r,1))); + $r->print(&show_grading_menu_form($symb)); return; } my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file; @@ -8323,7 +8694,7 @@ sub scantron_download_scantron_data { '','').'

'); - $r->print(&show_grading_menu_form(&get_symb($r,1))); + $r->print(&show_grading_menu_form($symb)); return ''; } @@ -8341,6 +8712,7 @@ sub checkscantron_results { my %record; my %scantron_config = &Apache::grades::get_scantron_config($env{'form.scantron_format'}); + my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config); my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile(); my $classlist=&Apache::loncoursedata::get_classlist(); my %idmap=&Apache::grades::username_to_idmap($classlist); @@ -8350,10 +8722,21 @@ sub checkscantron_results { return ''; } my $map=$navmap->getResourceByUrl($sequence); + my ($randomorder,@master_seq,%symb_to_resource); + if (ref($map)) { + $randomorder=$map->randomorder(); + } my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); + foreach my $resource (@resources) { + if (ref($resource)) { + my $ressymb = $resource->symb(); + push(@master_seq,$ressymb); + $symb_to_resource{$ressymb} = $resource; + } + } my (%grader_partids_by_symb,%grader_randomlists_by_symb); - &graders_resources_pass(\@resources,\%grader_partids_by_symb, \%grader_randomlists_by_symb); - + &graders_resources_pass(\@resources,\%grader_partids_by_symb, + \%grader_randomlists_by_symb,$bubbles_per_row); my ($uname,$udom); my (%scandata,%lastname,%bylast); $r->print(' @@ -8363,12 +8746,10 @@ sub checkscantron_results { my %completedstudents; my $count=&Apache::grades::get_todo_count($scanlines,$scan_data); - my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Bubblesheet/Submissions Comparison Status', - 'Progress of Bubblesheet Data/Submission Records Comparison',$count, - 'inline',undef,'checkscantron'); - my ($username,$domain,$started); + my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count); + my ($username,$domain,$started,%ordered); my $nav_error; - &scantron_get_maxbubble(\$nav_error); # Need the bubble lines array to parse. + &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse. if ($nav_error) { $r->print(&navmap_errormsg()); return ''; @@ -8410,15 +8791,34 @@ sub checkscantron_results { $scandata{$pid} = substr($line,$scantron_config{'Qstart'}-1,$lastpos); chomp($scandata{$pid}); $scandata{$pid} =~ s/\r$//; + my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION]; + my $user = $uname.':'.$usec; ($username,$domain)=split(/:/,$uname); + + my $scancode; + if ((exists($scan_record->{'scantron.CODE'})) && + (&Apache::lonnet::validCODE($scan_record->{'scantron.CODE'}))) { + $scancode = $scan_record->{'scantron.CODE'}; + } else { + $scancode = ''; + } + + my @mapresources = @resources; + if ($randomorder) { + @mapresources = + &users_order($user,$scancode,$sequence,\@master_seq,\%ordered, + \%symb_to_resource); + } my $counter = -1; - foreach my $resource (@resources) { + foreach my $resource (@mapresources) { my $parts; my $ressymb = $resource->symb(); if ((exists($grader_randomlists_by_symb{$ressymb})) || (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) { (my $analysis,$parts) = - &scantron_partids_tograde($resource,$env{'request.course.id'},$username,$domain); + &scantron_partids_tograde($resource,$env{'request.course.id'}, + $username,$domain,undef, + $bubbles_per_row); } else { $parts = $grader_partids_by_symb{$ressymb}; } @@ -8471,7 +8871,11 @@ sub checkscantron_results { $env{'form.scantron_maxbubble'}). '

' ); - $r->print('

'.&mt('Exact matches for [quant,_1,student].',$passed).'
'.&mt('Discrepancies detected for [quant,_1,student].',$failed).'

'); + $r->print('

' + .&mt('Exact matches for [_1][quant,_2,student][_3].','',$passed,'') + .'
' + .&mt('Discrepancies detected for [_1][quant,_2,student][_3].','',$failed,'') + .'

'); if ($passed) { $r->print(&mt('Students with exact correspondence between bubblesheet data and submissions are as follows:').'

'); $r->print(&Apache::loncommon::start_data_table()."\n". @@ -8657,6 +9061,13 @@ sub savedState { return \%savedState; } +#--- Href with symb and command --- + +sub href_symb_cmd { + my ($symb,$cmd)=@_; + return '/adm/grades?symb='.&HTML::Entities::encode(&Apache::lonenc::check_encrypt($symb),'<>&"').'&command='.$cmd; +} + sub grading_menu { my ($request) = @_; my ($symb)=&get_symb($request); @@ -8964,6 +9375,21 @@ sub init_perm { } } +sub init_old_essays { + my ($symb,$apath,$adom,$aname) = @_; + if ($symb ne '') { + my %essays = &Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname); + if (keys(%essays) > 0) { + $old_essays{$symb} = \%essays; + } + } + return; +} + +sub reset_old_essays { + undef(%old_essays); +} + sub gather_clicker_ids { my %clicker_ids; @@ -9065,7 +9491,8 @@ sub process_clicker { my $pincorrect=&mt("Percentage points for incorrect solution"); my $selectform=&Apache::loncommon::select_form($env{'form.upfiletype'},'upfiletype', {'iclicker' => 'i>clicker', - 'interwrite' => 'interwrite PRS'}); + 'interwrite' => 'interwrite PRS', + 'turning' => 'Turning Technologies'}); $symb = &Apache::lonenc::check_encrypt($symb); $result.=< @@ -9238,6 +9665,9 @@ ENDHEADER if ($env{'form.upfiletype'} eq 'interwrite') { ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses); } + if ($env{'form.upfiletype'} eq 'turning') { + ($errormsg,$number)=&turning_eval(\@questiontitles,\%responses); + } $result.='
'.&mt('Found [_1] question(s)',$number).'
'. ''. &mt('Awarding [_1] percent for correct and [_2] percent for incorrect responses', @@ -9369,6 +9799,31 @@ sub interwrite_eval { return ($errormsg,$number); } +sub turning_eval { + my ($questiontitles,$responses)=@_; + my $number=0; + my $errormsg=''; + foreach my $line (split(/[\n\r]/,$env{'form.upfile'})) { + my %components=&Apache::loncommon::record_sep($line); + my @entries=map {$components{$_}} (sort(keys(%components))); + if ($#entries>$number) { $number=$#entries; } + my $id=$entries[0]; + my @idresponses; + $id=~s/^[\#0]+//; + unless ($id) { next; } + for (my $idx=1;$idx<=$#entries;$idx++) { + $entries[$idx]=~s/\,/\;/g; + $entries[$idx]=~s/[^a-zA-Z0-9\.\*\-\+\;]+//g; + push(@idresponses,$entries[$idx]); + } + $$responses{$id}=join(',',@idresponses); + } + for (my $i=1; $i<=$number; $i++) { + $$questiontitles[$i]=&mt('Question [_1]',$i); + } + return ($errormsg,$number); +} + sub assign_clicker_grades { my ($r)=@_; my ($symb)=&get_symb($r); @@ -9510,6 +9965,19 @@ sub navmap_errormsg { ''; } +sub startpage { + my ($r,$symb,$crumbs,$onlyfolderflag,$nodisplayflag,$stuvcurrent,$stuvdisp,$nomenu,$js) = @_; + if ($nomenu) { + $r->print(&Apache::loncommon::start_page("Student's Version",$js,{'only_body' => '1'})); + } else { + $r->print(&Apache::loncommon::start_page('Grading',$js, + {'bread_crumbs' => $crumbs})); + } + unless ($nodisplayflag) { + $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp)); + } +} + sub handler { my $request=$_[0]; &reset_caches(); @@ -9531,7 +9999,7 @@ sub handler { $ssi_error = 0; my $brcrum = [{href=>"/adm/grades",text=>"Grading"}]; my $start_page = &Apache::loncommon::start_page('Grading',undef, - {'bread_crumbs' => $brcrum}); + {'bread_crumbs' => $brcrum}); if ($symb eq '' && $command eq '') { if ($env{'user.adv'}) { &Apache::loncommon::content_type($request,'text/html'); @@ -9564,6 +10032,7 @@ sub handler { &init_perm(); if (!%perm) { $request->internal_redirect('/adm/quickgrades'); + return OK; } else { &Apache::loncommon::content_type($request,'text/html'); $request->send_http_header; @@ -9584,9 +10053,33 @@ sub handler { } &Apache::loncommon::content_type($request,'text/html'); $request->send_http_header; - $request->print($start_page); + unless ((($command eq 'submission' || $command eq 'versionsub')) && ($perm{'vgr'})) { + $request->print($start_page); + } if ($command eq 'submission' && $perm{'vgr'}) { + my ($stuvcurrent,$stuvdisp,$versionform,$js); + if (($env{'form.student'} ne '') && ($env{'form.userdom'} ne '')) { + ($stuvcurrent,$stuvdisp,$versionform,$js) = + &choose_task_version_form($symb,$env{'form.student'}, + $env{'form.userdom'}); + } + &startpage($request,$symb,[{href=>"", text=>"Student Submissions"}],undef,undef,$stuvcurrent,$stuvdisp,undef,$js); + if ($versionform) { + $request->print($versionform); + } + $request->print('
'); ($env{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0)); + } elsif ($command eq 'versionsub' && $perm{'vgr'}) { + my ($stuvcurrent,$stuvdisp,$versionform,$js) = + &choose_task_version_form($symb,$env{'form.student'}, + $env{'form.userdom'}, + $env{'form.inhibitmenu'}); + &startpage($request,$symb,[{href=>"", text=>"Previous Student Version"}],undef,undef,$stuvcurrent,$stuvdisp,$env{'form.inhibitmenu'},$js); + if ($versionform) { + $request->print($versionform); + } + $request->print('
'); + $request->print(&show_previous_task_version($request,$symb)); } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) { &pickStudentPage($request); } elsif ($command eq 'displayPage' && $perm{'vgr'}) { @@ -9766,6 +10259,8 @@ ssi_with_retries() calling routine should trap the error condition and display the warning found in &navmap_errormsg(). + $scantron_config - Reference to bubblesheet format configuration hash. + Returns the maximum number of bubble lines that are expected to occur. Does this by walking the selected sequence rendering the resource and then checking &Apache::lonxml::get_problem_counter()