--- loncom/homework/grades.pm 2009/12/27 01:00:44 1.574.2.4 +++ loncom/homework/grades.pm 2010/05/22 01:01:03 1.574.2.12 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.574.2.4 2009/12/27 01:00:44 raeburn Exp $ +# $Id: grades.pm,v 1.574.2.12 2010/05/22 01:01:03 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -97,9 +97,15 @@ sub ssi_print_error { # # --- Retrieve the parts from the metadata file.--- sub getpartlist { - my ($symb) = @_; + my ($symb,$errorref) = @_; my $navmap = Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + if (ref($errorref)) { + $$errorref = 'navmap'; + return; + } + } my $res = $navmap->getBySymb($symb); my $partlist = $res->parts(); my $url = $res->src(); @@ -144,9 +150,15 @@ sub nameUserString { #--- Get the partlist and the response type for a given problem. --- #--- Indicate if a response type is coded handgraded or not. --- sub response_type { - my ($symb) = shift; + my ($symb,$response_error) = @_; my $navmap = Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + if (ref($response_error)) { + $$response_error = 1; + } + return; + } my $res = $navmap->getBySymb($symb); my $partlist = $res->parts(); my %vPart = @@ -193,12 +205,17 @@ sub get_display_part { #--- Show resource title #--- and parts and response type sub showResourceInfo { - my ($symb,$probTitle,$checkboxes) = @_; + my ($symb,$probTitle,$checkboxes,$res_error) = @_; my $col=3; if ($checkboxes) { $col=4; } my $result = '

'.&mt('Current Resource').': '.$probTitle.'

'."\n"; + my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error); + if (ref($res_error)) { + if ($$res_error) { + return; + } + } $result .=''; - my ($partlist,$handgrade,$responseType) = &response_type($symb); my %resptype = (); my $hdgrade='no'; my %partsseen; @@ -754,7 +771,7 @@ sub verifyreceipt { my $title.= '

'. - &mt('Verifying Receipt No. [_1]',$receipt). + &mt('Verifying Receipt No. [_1]',$receipt). '

'."\n". '

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

'."\n"; @@ -766,7 +783,13 @@ sub verifyreceipt { if ($env{"course.$courseid.receiptalg"} eq 'receipt2' || $env{"course.$courseid.receiptalg"} eq 'receipt3') { $receiptparts=1; } my $parts=['0']; - if ($receiptparts) { ($parts)=&response_type($symb); } + if ($receiptparts) { + my $res_error; + ($parts)=&response_type($symb,\$res_error); + if ($res_error) { + return &navmap_errormsg(); + } + } my $header = &Apache::loncommon::start_data_table(). @@ -808,11 +831,11 @@ sub verifyreceipt { } } if ($matches == 0) { - $string = $title.&mt('No match found for the above receipt.'); + $string = $title.&mt('No match found for the above receipt number.'); } else { $string = &jscriptNform($symb).$title. '

'. - &mt('The above receipt matches the following [numerate,_1,student].',$matches). + &mt('The above receipt number matches the following [numerate,_1,student].',$matches). '

'. $header. $contents. @@ -1731,8 +1754,6 @@ sub gradeBox { } $line.=''."\n"; - - #&mt('',$display_part,$radio,$line); $result .= ''; $result.='
Part:[_1]Points:[_2]or[_3]'.&mt('Part').':'.$display_part.''.&mt('Points').':'.$radio.''.&mt('or').''.$line.'
'."\n"; @@ -1744,15 +1765,19 @@ sub gradeBox { $$record{'resource.'.$partid.'.tries'}.'" />'."\n". ''."\n"; - $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record); + my $res_error; + $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record,\$res_error); + if ($res_error) { + return &navmap_errormsg(); + } return $result; } sub handback_box { - my ($symb,$uname,$udom,$counter,$partid,$record) = @_; - my ($partlist,$handgrade,$responseType) = &response_type($symb); + my ($symb,$uname,$udom,$counter,$partid,$record,$res_error) = @_; + my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error); my (@respids); - my @part_response_id = &flatten_responseType($responseType); + my @part_response_id = &flatten_responseType($responseType); foreach my $part_response_id (@part_response_id) { my ($part,$resp) = @{ $part_response_id }; if ($part eq $partid) { @@ -2045,7 +2070,12 @@ KEYWORDS } my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname); - my ($partlist,$handgrade,$responseType) = &response_type($symb); + my $res_error; + my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error); + if ($res_error) { + $request->print(&navmap_errormsg()); + return; + } # Display student info $request->print(($counter == 0 ? '' : '
')); @@ -2592,7 +2622,12 @@ sub processHandGrade { } $ctr = 0; @parsedlist = reverse @parsedlist if ($button eq 'Previous'); - my ($partlist) = &response_type($symb); + my $res_error; + my ($partlist) = &response_type($symb,\$res_error); + if ($res_error) { + $request->print(&navmap_errormsg()); + return; + } foreach my $student (@parsedlist) { my $submitonly=$env{'form.submitonly'}; my ($uname,$udom) = split(/:/,$student); @@ -2790,8 +2825,12 @@ sub check_and_remove_from_queue { sub handback_files { my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_; my $portfolio_root = '/userfiles/portfolio'; - my ($partlist,$handgrade,$responseType) = &response_type($symb); - + my $res_error; + my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error); + if ($res_error) { + $request->print('
'.&navmap_errormsg().'
'); + return; + } my @part_response_id = &flatten_responseType($responseType); foreach my $part_response_id (@part_response_id) { my ($part_id,$resp_id) = @{ $part_response_id }; @@ -3249,7 +3288,11 @@ sub viewgrades { $result.= '

'.$common_header.'

'.&Apache::loncommon::start_data_table(); #radio buttons/text box for assigning points for a section or class. #handles different parts of a problem - my ($partlist,$handgrade,$responseType) = &response_type($symb); + my $res_error; + my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error); + if ($res_error) { + return &navmap_errormsg(); + } my %weight = (); my $ctsparts = 0; my %seen = (); @@ -3309,7 +3352,11 @@ sub viewgrades { &Apache::loncommon::start_data_table_header_row(). ''.&mt('No.').''. ''.&nameUserString('header')."\n"; - my (@parts) = sort(&getpartlist($symb)); + my $partserror; + my (@parts) = sort(&getpartlist($symb,\$partserror)); + if ($partserror) { + return &navmap_errormsg(); + } my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb); my @partids = (); foreach my $part (@parts) { @@ -3468,7 +3515,11 @@ sub editgrades { my %columns = (); my ($i,$ctr,$count,$rec_update) = (0,0,0,0); - my (@parts) = sort(&getpartlist($symb)); + my $partserror; + my (@parts) = sort(&getpartlist($symb,\$partserror)); + if ($partserror) { + return &navmap_errormsg(); + } my $header; while ($ctr < $env{'form.totalparts'}) { my $partid = $env{'form.partid_'.$ctr}; @@ -3800,8 +3851,14 @@ ENDPICK } sub csvupload_fields { - my ($symb) = @_; - my (@parts) = &getpartlist($symb); + my ($symb,$errorref) = @_; + my (@parts) = &getpartlist($symb,$errorref); + if (ref($errorref)) { + if ($$errorref) { + return; + } + } + my @fields=(['ID','Student/Employee ID'], ['username','Student Username'], ['domain','Student Domain']); @@ -3901,8 +3958,12 @@ sub csvuploadmap { &csvuploadmap_header($request,$symb,$datatoken,$#records+1); my ($i,$keyfields); if (@records) { - my @fields=&csvupload_fields($symb); - + my $fieldserror; + my @fields=&csvupload_fields($symb,\$fieldserror); + if ($fieldserror) { + $request->print(&navmap_errormsg()); + return; + } if ($env{'form.upfile_associate'} eq 'reverse') { &Apache::loncommon::csv_print_samples($request,\@records); $i=&Apache::loncommon::csv_print_select_table($request,\@records, @@ -4138,7 +4199,12 @@ LISTJAVASCRIPT &mt('Manual Grading by Page or Sequence').''; $result.='
'."\n"; - my ($titles,$symbx) = &getSymbMap(); + my $map_error; + my ($titles,$symbx) = &getSymbMap($map_error); + if ($map_error) { + $request->print(&navmap_errormsg()); + return; + } my ($curpage) =&Apache::lonnet::decode_symb($symb); # my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb); # my $type=($curpage =~ /\.(page|sequence)/); @@ -4233,8 +4299,14 @@ LISTJAVASCRIPT } sub getSymbMap { + my ($map_error) = @_; my $navmap = Apache::lonnavmaps::navmap->new(); - + unless (ref($navmap)) { + if (ref($map_error)) { + $$map_error = 'navmap'; + } + return; + } my %symbx = (); my @titles = (); my $minder = 0; @@ -4293,6 +4365,11 @@ sub displayPage { $request->print($result); my $navmap = Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + $request->print(&navmap_errormsg()); + $request->print(&show_grading_menu_form($symb)); + return; + } my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($env{'form.page'}); my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps if (!$map) { @@ -4544,6 +4621,10 @@ sub updateGradeByPage { $request->print($result); my $navmap = Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + $request->print(&navmap_errormsg()); + return; + } my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $env{'form.page'}); my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps if (!$map) { @@ -4688,7 +4769,7 @@ sub updateGradeByPage { # #------------------------------------------------------------------- -#--------------------Scantron Grading----------------------------------- +#--------------------Bubblesheet (Scantron) Grading----------------------------------- # #------ start of section for handling grading by page/sequence --------- @@ -4747,7 +4828,9 @@ the homework problem. Returns html hidden inputs used to hold context/default values. Arguments: - $symb - $symb of the current resource + $symb - $symb of the current resource + $map_error - ref to scalar which will container error if + $navmap object is unavailable in &getSymbMap(). =cut @@ -4771,9 +4854,12 @@ sub defaultFormData { =cut sub getSequenceDropDown { - my ($symb)=@_; + my ($symb,$map_error)=@_; my $result=''."\n". - ''."\n". + ''."\n". ''."\n". &Apache::loncommon::end_data_table_row()."\n". &Apache::loncommon::end_data_table()."\n". @@ -6213,7 +6304,12 @@ sub scantron_validate_file { $r->print('

'.&mt('Gathering necessary information.').'

');$r->rflush(); #get the student pick code ready $r->print(&Apache::loncommon::studentbrowser_javascript()); - my $max_bubble=&scantron_get_maxbubble(); + my $nav_error; + my $max_bubble=&scantron_get_maxbubble(\$nav_error); + if ($nav_error) { + $r->print(&navmap_errormsg()); + return ''; + } my $result=&scantron_form_start($max_bubble).$default_form_data; $r->print($result); @@ -6625,6 +6721,10 @@ sub scantron_validate_sequence { my ($r,$currentphase) = @_; my $navmap=Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + $r->print(&navmap_errormsg()); + return (1,$currentphase); + } my (undef,undef,$sequence)= &Apache::lonnet::decode_symb($env{'form.selectpage'}); @@ -6657,7 +6757,12 @@ sub scantron_validate_ID { my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); my ($scanlines,$scan_data)=&scantron_getfile(); - &scantron_get_maxbubble(); # parse needs the bubble_lines.. array. + my $nav_error; + &scantron_get_maxbubble(\$nav_error); # parse needs the bubble_lines.. array. + if ($nav_error) { + $r->print(&navmap_errormsg()); + return(1,$currentphase); + } my %found=('ids'=>{},'usernames'=>{}); for (my $i=0;$i<=$scanlines->{'count'};$i++) { @@ -7210,7 +7315,12 @@ sub scantron_validate_CODE { my %allcodes=&get_codes(); - &scantron_get_maxbubble(); # parse needs the lines per response array. + my $nav_error; + &scantron_get_maxbubble(\$nav_error); # parse needs the lines per response array. + if ($nav_error) { + $r->print(&navmap_errormsg()); + return(1,$currentphase); + } my ($scanlines,$scan_data)=&scantron_getfile(); for (my $i=0;$i<=$scanlines->{'count'};$i++) { @@ -7264,7 +7374,12 @@ sub scantron_validate_doublebubble { #get scantron line setup my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); my ($scanlines,$scan_data)=&scantron_getfile(); - &scantron_get_maxbubble(); # parse needs the bubble line array. + my $nav_error; + &scantron_get_maxbubble(\$nav_error); # parse needs the bubble line array. + if ($nav_error) { + $r->print(&navmap_errormsg()); + return(1,$currentphase); + } for (my $i=0;$i<=$scanlines->{'count'};$i++) { my $line=&scantron_get_line($scanlines,$scan_data,$i); @@ -7282,6 +7397,8 @@ sub scantron_validate_doublebubble { sub scantron_get_maxbubble { + my ($nav_error) = @_; + if (defined($env{'form.scantron_maxbubble'}) && $env{'form.scantron_maxbubble'}) { &restore_bubble_lines(); @@ -7292,6 +7409,12 @@ sub scantron_get_maxbubble { &Apache::lonnet::decode_symb($env{'form.selectpage'}); my $navmap=Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + if (ref($nav_error)) { + $$nav_error = 1; + } + return; + } my $map=$navmap->getResourceByUrl($sequence); my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); @@ -7381,7 +7504,11 @@ sub scantron_validate_missingbubbles { #get scantron line setup my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); my ($scanlines,$scan_data)=&scantron_getfile(); - my $max_bubble=&scantron_get_maxbubble(); + my $nav_error; + my $max_bubble=&scantron_get_maxbubble(\$nav_error); + if ($nav_error) { + return(1,$currentphase); + } if (!$max_bubble) { $max_bubble=2**31; } for (my $i=0;$i<=$scanlines->{'count'};$i++) { my $line=&scantron_get_line($scanlines,$scan_data,$i); @@ -7441,13 +7568,24 @@ sub scantron_process_students { my $classlist=&Apache::loncoursedata::get_classlist(); my %idmap=&username_to_idmap($classlist); my $navmap=Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + $r->print(&navmap_errormsg()); + return ''; + } my $map=$navmap->getResourceByUrl($sequence); my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); my (%grader_partids_by_symb,%grader_randomlists_by_symb); &graders_resources_pass(\@resources,\%grader_partids_by_symb, \%grader_randomlists_by_symb); + my $resource_error; foreach my $resource (@resources) { - my $ressymb = $resource->symb(); + my $ressymb; + if (ref($resource)) { + $ressymb = $resource->symb(); + } else { + $resource_error = 1; + last; + } my ($analysis,$parts) = &scantron_partids_tograde($resource,$env{'request.course.id'}, $env{'user.name'},$env{'user.domain'},1); @@ -7459,6 +7597,10 @@ sub scantron_process_students { } } } + if ($resource_error) { + $r->print(&navmap_errormsg()); + return ''; + } my ($uname,$udom); my $result= <print(&navmap_errormsg()); + return ''; + } # If an ssi failed in scantron_get_maxbubble, put an error message out to # the user and return. @@ -7524,9 +7671,15 @@ SCANTRONFORM } ($uname,$udom)=split(/:/,$uname); - my %partids_by_symb; + my (%partids_by_symb,$res_error); foreach my $resource (@resources) { - my $ressymb = $resource->symb(); + my $ressymb; + if (ref($resource)) { + $ressymb = $resource->symb(); + } else { + $res_error = 1; + last; + } if ((exists($grader_randomlists_by_symb{$ressymb})) || (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) { my ($analysis,$parts) = @@ -7537,6 +7690,12 @@ SCANTRONFORM } } + if ($res_error) { + &scantron_add_delay(\@delayqueue,$line, + 'An error occurred while grading student '.$uname,2); + next; + } + &Apache::lonxml::clear_problem_counter(); &Apache::lonnet::appenv($scan_record); @@ -7760,7 +7919,7 @@ sub scantron_upload_scantron_data { &Apache::lonhtmlcommon::end_pick_box().'
- + '); return ''; @@ -7778,7 +7937,7 @@ sub scantron_upload_scantron_data_save { if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) && !&Apache::lonnet::allowed('usc', $env{'form.domainid'}.'_'.$env{'form.courseid'})) { - $r->print(&mt("You are not allowed to upload Scantron data to the requested course.")."
"); + $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."
"); if ($symb) { $r->print(&show_grading_menu_form($symb)); } else { @@ -7967,6 +8126,10 @@ sub checkscantron_results { my $classlist=&Apache::loncoursedata::get_classlist(); my %idmap=&Apache::grades::username_to_idmap($classlist); my $navmap=Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + $r->print(&navmap_errormsg()); + return ''; + } my $map=$navmap->getResourceByUrl($sequence); my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); my (%grader_partids_by_symb,%grader_randomlists_by_symb); @@ -7981,12 +8144,16 @@ sub checkscantron_results { my %completedstudents; my $count=&Apache::grades::get_todo_count($scanlines,$scan_data); - my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron/Submissions Comparison Status', - 'Progress of Scantron Data/Submission Records Comparison',$count, + 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); - - &scantron_get_maxbubble(); # Need the bubble lines array to parse. + my $nav_error; + &scantron_get_maxbubble(\$nav_error); # Need the bubble lines array to parse. + if ($nav_error) { + $r->print(&navmap_errormsg()); + return ''; + } &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state, 'Processing first student'); @@ -8059,14 +8226,14 @@ sub checkscantron_results { if ($scandata{$pid} eq $record{$pid}) { my $css_class = ($passed % 2)?'LC_odd_row':'LC_even_row'; $okstudents .= ''. -''.&mt('Scantron').''.$showscandata.''.$last.''.$pid.''."\n". +''.&mt('Bubblesheet').''.$showscandata.''.$last.''.$pid.''."\n". ''."\n". ''."\n". 'Submissions'.$showrecord.''."\n"; $passed ++; } else { my $css_class = ($failed % 2)?'LC_odd_row':'LC_even_row'; - $badstudents .= ''.&mt('Scantron').''.$scandata{$pid}.''.$last.''.$pid.''."\n". + $badstudents .= ''.&mt('Bubblesheet').''.$scandata{$pid}.''.$last.''.$pid.''."\n". ''."\n". ''."\n". 'Submissions'.$record{$pid}.''."\n". @@ -8300,13 +8467,13 @@ sub grading_menu { $fields{'command'} = 'scantron_selectphase'; $url = &Apache::lonhtmlcommon::build_url('grades/',\%fields); push(@menu, { url => $url, - name => &mt('Grade/Manage/Review Scantron Forms'), + name => &mt('Grade/Manage/Review Bubblesheets'), short_description => - &mt('Grade scantron exams, upload/download scantron data files, and review previously graded scantron exams.')}); + &mt('Grade bubblesheet exams, upload/download bubblesheet data files, and review previously graded bubblesheet exams.')}); $fields{'command'} = 'verify'; $url = &Apache::lonhtmlcommon::build_url('grades/',\%fields); push(@menu, { url => "", - name => &mt('Verify Receipt'), + name => &mt('Verify Receipt No.'), short_description => &mt('')}); # Create the menu @@ -8321,14 +8488,14 @@ sub grading_menu { ''."\n". ''."\n"; foreach my $menudata (@menu) { - if ($menudata->{'name'} ne &mt('Verify Receipt')) { + if ($menudata->{'name'} ne &mt('Verify Receipt No.')) { $Str .='

{'jscript'}. ' href="'. $menudata->{'url'}.'" >'. $menudata->{'name'}."

\n"; } else { - $Str .='
{'jscript'}. ' onClick="javascript:checkChoice(document.forms.gradingMenu,\'5\',\'verify\')" '. ' /> '. @@ -8986,7 +9153,11 @@ sub assign_clicker_grades { my ($symb)=&get_symb($r); if (!$symb) {return '';} # See which part we are saving to - my ($partlist,$handgrade,$responseType) = &response_type($symb); + my $res_error; + my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error); + if ($res_error) { + return &navmap_errormsg(); + } # FIXME: This should probably look for the first handgradeable part my $part=$$partlist[0]; # Start screen output @@ -9050,13 +9221,13 @@ ENDHEADER my $sum=0; my $realnumber=$number; for (my $i=0;$i<$number;$i++) { - if ($answer[$i]) { + if ($correct[$i] eq '-') { + $realnumber--; + } elsif ($answer[$i]) { if ($gradingmechanism eq 'attendance') { $sum+=$pcorrect; - } elsif ($answer[$i] eq '*') { + } elsif ($correct[$i] eq '*') { $sum+=$pcorrect; - } elsif ($answer[$i] eq '-') { - $realnumber--; } else { if ($answer[$i] eq $correct[$i]) { $sum+=$pcorrect; @@ -9090,6 +9261,13 @@ ENDHEADER return $result.&show_grading_menu_form($symb); } +sub navmap_errormsg { + return '
'. + &mt('An error occurred retrieving information about resources in the course.').'
'. + &mt('It is recommended that you [_1]re-initialize the course[_2] and then return to this grading page..','',''). + '
'; +} + sub handler { my $request=$_[0]; &reset_caches(); @@ -9315,6 +9493,13 @@ ssi_with_retries() =item scantron_get_maxbubble() : + Arguments: + $nav_error - Reference to scalar which is a flag to indicate a + failure to retrieve a navmap object. + if $nav_error is set to 1 by scantron_get_maxbubble(), the + calling routine should trap the error condition and display the warning + found in &navmap_errormsg(). + 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() @@ -9380,6 +9565,11 @@ ssi_with_retries() Validates all scanlines in the selected file to not have any invalid or underspecified student/employee IDs +=item navmap_errormsg() : + + Returns HTML mark-up inside a
with a link to re-initialize the course. + Should be called whenever the request to instantiate a navmap object fails. + =back =cut