--- loncom/homework/grades.pm 2009/03/18 12:26:21 1.558 +++ loncom/homework/grades.pm 2009/12/27 01:25:26 1.586 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.558 2009/03/18 12:26:21 bisitz Exp $ +# $Id: grades.pm,v 1.586 2009/12/27 01:25:26 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 = @@ -183,7 +195,8 @@ sub get_display_part { my ($partID,$symb)=@_; my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb); if (defined($display) and $display ne '') { - $display.= " (<span class=\"LC_internal_info\">id $partID</span>)"; + $display.= ' (<span class="LC_internal_info">' + .&mt('Part ID: [_1]',$partID).'</span>)'; } else { $display=$partID; } @@ -193,37 +206,49 @@ sub get_display_part { #--- Show resource title #--- and parts and response type sub showResourceInfo { - my ($symb,$probTitle,$checkboxes) = @_; - my $col=3; - if ($checkboxes) { $col=4; } + my ($symb,$probTitle,$checkboxes,$res_error) = @_; my $result = '<h3>'.&mt('Current Resource').': '.$probTitle.'</h3>'."\n"; - $result .='<table border="0">'; - my ($partlist,$handgrade,$responseType) = &response_type($symb); + my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error); + if (ref($res_error)) { + if ($$res_error) { + return; + } + } + $result.=&Apache::loncommon::start_data_table() + .&Apache::loncommon::start_data_table_header_row(); + if ($checkboxes) { + $result.='<th> </th>'; + } + $result.='<th>'.&mt('Problem Part').'</th>' + .'<th>'.&mt('Res. ID').'</th>' + .'<th>'.&mt('Type').'</th>' + .&Apache::loncommon::end_data_table_header_row(); my %resptype = (); my $hdgrade='no'; my %partsseen; foreach my $partID (sort(keys(%$responseType))) { - foreach my $resID (sort(keys(%{ $responseType->{$partID} }))) { - my $handgrade=$$handgrade{$partID.'_'.$resID}; - my $responsetype = $responseType->{$partID}->{$resID}; - $hdgrade = $handgrade if ($handgrade eq 'yes'); - $result.='<tr>'; - if ($checkboxes) { - if (exists($partsseen{$partID})) { - $result.="<td> </td>"; - } else { - $result.="<td><input type='checkbox' name='vPart' value='$partID' checked='checked' /></td>"; - } - $partsseen{$partID}=1; - } - my $display_part=&get_display_part($partID,$symb); - $result.='<td><b>'.&mt('Part').': </b>'.$display_part. - ' <span class="LC_internal_info">'.$resID.'</span></td>'. - '<td><b>'.&mt('Type').': </b>'.$responsetype.'</td></tr>'; -# '<td>'.&mt('<b>Handgrade: </b>[_1]',$handgrade).'</td></tr>'; - } + foreach my $resID (sort(keys(%{ $responseType->{$partID} }))) { + my $handgrade=$$handgrade{$partID.'_'.$resID}; + my $responsetype = $responseType->{$partID}->{$resID}; + $hdgrade = $handgrade if ($handgrade eq 'yes'); + $result.=&Apache::loncommon::start_data_table_row(); + if ($checkboxes) { + if (exists($partsseen{$partID})) { + $result.="<td> </td>"; + } else { + $result.="<td><input type='checkbox' name='vPart' value='$partID' checked='checked' /></td>"; + } + $partsseen{$partID}=1; + } + my $display_part=&get_display_part($partID,$symb); + $result.='<td>'.$display_part.'</td>' + .'<td>'.'<span class="LC_internal_info">'.$resID.'</span></td>' + .'<td>'.&mt($responsetype).'</td>' +# .'<td>'.&mt('<b>Handgrade: </b>[_1]',$handgrade).'</td>' + .&Apache::loncommon::end_data_table_row(); + } } - $result.='</table>'."\n"; + $result.=&Apache::loncommon::end_data_table(); return $result,$responseType,$hdgrade,$partlist,$handgrade; } @@ -754,7 +779,7 @@ sub verifyreceipt { my $title.= '<h3><span class="LC_info">'. - &mt('Verifying Receipt No. [_1]',$receipt). + &mt('Verifying Receipt No. [_1]',$receipt). '</span></h3>'."\n". '<h4>'.&mt('<b>Resource: </b>[_1]',$env{'form.probTitle'}). '</h4>'."\n"; @@ -766,7 +791,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 +839,14 @@ sub verifyreceipt { } } if ($matches == 0) { - $string = $title.&mt('No match found for the above receipt.'); + $string = $title + .'<p class="LC_warning">' + .&mt('No match found for the above receipt number.') + .'</p>'; } else { $string = &jscriptNform($symb).$title. '<p>'. - &mt('The above receipt matches the following [numerate,_1,student].',$matches). + &mt('The above receipt number matches the following [quant,_1,student].',$matches). '</p>'. $header. $contents. @@ -844,12 +878,10 @@ sub listStudents { my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes')); - my %lt = ( 'multiple' => - &mt("Please select a student or group of students before clicking on the Next button."), - 'single' => - &mt("Please select the student before clicking on the Next button."), - ); - %lt = &Apache::lonlocal::texthash(%lt); + my %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.', + ); $request->print(<<LISTJAVASCRIPT); <script type="text/javascript" language="javascript"> function checkSelect(checkBox) { @@ -891,16 +923,17 @@ LISTJAVASCRIPT my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'. "\n".$table; - $gradeTable .= - ' <b>'.&mt('View Problem Text').': </b>'. - '<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n". - '<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n". - '<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"; - $gradeTable .= - ' <b>'.&mt('View Answer').': </b>'. - '<label><input type="radio" name="vAns" value="no" /> '.&mt('no').' </label>'."\n". - '<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n". - '<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"; + $gradeTable .= &Apache::lonhtmlcommon::start_pick_box(); + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text')) + .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n" + .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n" + .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n" + .&Apache::lonhtmlcommon::row_closure(); + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer')) + .'<label><input type="radio" name="vAns" value="no" /> '.&mt('no').' </label>'."\n" + .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n" + .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n" + .&Apache::lonhtmlcommon::row_closure(); my $submission_options; if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) { @@ -915,19 +948,20 @@ LISTJAVASCRIPT '<label><input type="radio" name="lastSub" value="last" /> '.&mt('last submission & parts info').' </label>'."\n". '<label><input type="radio" name="lastSub" value="datesub" /> '.&mt('by dates and submissions').' </label>'."\n". '<label><input type="radio" name="lastSub" value="all" /> '.&mt('all details').'</label>'; - $gradeTable .= - ' <b>'.&mt('Submissions').': </b>'.$submission_options.'<br />'."\n"; + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Submissions')) + .$submission_options + .&Apache::lonhtmlcommon::row_closure(); + + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments')) + .'<select name="increment">' + .'<option value="1">'.&mt('Whole Points').'</option>' + .'<option value=".5">'.&mt('Half Points').'</option>' + .'<option value=".25">'.&mt('Quarter Points').'</option>' + .'<option value=".1">'.&mt('Tenths of a Point').'</option>' + .'</select>' + .&Apache::lonhtmlcommon::row_closure(); $gradeTable .= - ' <b>'.&mt('Grading Increments').': </b>'. - '<select name="increment">'. - '<option value="1">'.&mt('Whole Points').'</option>'. - '<option value=".5">'.&mt('Half Points').'</option>'. - '<option value=".25">'.&mt('Quarter Points').'</option>'. - '<option value=".1">'.&mt('Tenths of a Point').'</option>'. - '</select>'; - - $gradeTable .= &build_section_inputs(). '<input type="hidden" name="submitonly" value="'.$submitonly.'" />'."\n". '<input type="hidden" name="handgrade" value="'.$env{'form.handgrade'}.'" /><br />'."\n". @@ -938,14 +972,23 @@ LISTJAVASCRIPT '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n"; if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) { - $gradeTable.='<input type="hidden" name="Status" value="'.$stu_status.'" />'."\n"; + $gradeTable .= '<input type="hidden" name="Status" value="'.$stu_status.'" />'."\n"; } else { - $gradeTable.=&mt('<b>Student Status:</b> [_1]', - &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);')).'<br />'; + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status')) + .&Apache::lonhtmlcommon::StatusOptions( + $saveStatus,undef,1,'javascript:reLoadList(this.form);') + .&Apache::lonhtmlcommon::row_closure(); } - $gradeTable.=&mt('To '.lc($viewgrade)." a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.").'<br />'."\n". - '<input type="hidden" name="command" value="processGroup" />'."\n"; + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism')) + .'<input type="checkbox" name="checkPlag" checked="checked" />' + .&Apache::lonhtmlcommon::row_closure(1) + .&Apache::lonhtmlcommon::end_pick_box(); + + $gradeTable .= '<p>' + .&mt('To '.lc($viewgrade)." a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.")."\n" + .'<input type="hidden" name="command" value="processGroup" />' + .'</p>'; # checkall buttons $gradeTable.=&check_script('gradesub', 'stuinfo'); @@ -953,7 +996,6 @@ LISTJAVASCRIPT 'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n". 'value="'.&mt('Next').' →" /> <br />'."\n"; $gradeTable.=&check_buttons(); - $gradeTable.='<label><input type="checkbox" name="checkPlag" checked="checked" />'.&mt('Check For Plagiarism').'</label>'; my ($classlist, undef, $fullname) = &getclasslist($getsec,'1',$getgroup); $gradeTable.= &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(); @@ -1036,7 +1078,7 @@ LISTJAVASCRIPT $gradeTable.= &Apache::loncommon::start_data_table_row(); } $gradeTable.='<td align="right">'.$ctr.' </td>'. - '<td align="center"><label><input type=checkbox name="stuinfo" value="'. + '<td align="center"><label><input type="checkbox" name="stuinfo" value="'. $student.':'.$$fullname{$student}.':::SECTION'.$section. ') " /> </label></td>'."\n".'<td>'. &nameUserString(undef,$$fullname{$student},$uname,$udom). @@ -1543,8 +1585,8 @@ INNERJS pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">"); pDoc.write("<h3><span class=\\"LC_info\\"> Compose Message for \"+fullname+\"<\\/span><\\/h3><br /><br />"); - pDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">"); - pDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">"); + pDoc.write('<table border="0" width="100%"><tr><td bgcolor="#777777">'); + pDoc.write('<table border="0" width="100%"><tr bgcolor="#DDFFFF">'); pDoc.write("<td><b>Type<\\/b><\\/td><td><b>Include<\\/b><\\/td><td><b>Message<\\/td><\\/tr>"); } function displaySubject(msg,shwsel) { @@ -1628,8 +1670,8 @@ INNERJS hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">"); hDoc.write("<h3><span class=\\"LC_info\\"> Keyword Highlight Options<\\/span><\\/h3><br /><br />"); - hDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">"); - hDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">"); + hDoc.write('<table border="0" width="100%"><tr><td bgcolor="#777777">'); + hDoc.write('<table border="0" width="100%"><tr bgcolor="#DDFFFF">'); hDoc.write("<td><b>Text Color<\\/b><\\/td><td><b>Font Size<\\/b><\\/td><td><b>Font Style<\\/td><\\/tr>"); } @@ -1669,6 +1711,25 @@ sub get_increment { return $increment; } +sub gradeBox_start { + return ( + &Apache::loncommon::start_data_table() + .&Apache::loncommon::start_data_table_header_row() + .'<th>'.&mt('Part').'</th>' + .'<th>'.&mt('Points').'</th>' + .'<th> </th>' + .'<th>'.&mt('Assign Grade').'</th>' + .'<th>'.&mt('Weight').'</th>' + .'<th>'.&mt('Grade Status').'</th>' + .&Apache::loncommon::end_data_table_header_row() + ); +} + +sub gradeBox_end { + return ( + &Apache::loncommon::end_data_table() + ); +} #--- displays the grading box, used in essay type problem and grading by page/sequence sub gradeBox { my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_; @@ -1688,7 +1749,7 @@ sub gradeBox { if ($last_resets{$partid}) { $aggtries = &get_num_tries($record,$last_resets{$partid},$partid); } - $result.='<table border="0"><tr>'; + $result.=&Apache::loncommon::start_data_table_row(); my $ctr = 0; my $thisweight = 0; my $increment = &get_increment(); @@ -1711,8 +1772,8 @@ sub gradeBox { $wgt.')" /></td>'."\n"; $line.='<td>/'.$wgt.' '.$wgtmsg. ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? ' '.$checkIcon : ''). - ' </td><td><b>'.&mt('Grade Status').':</b>'."\n"; - $line.='<select name="GD_SEL'.$counter.'_'.$partid.'" '. + ' </td>'."\n"; + $line.='<td><select name="GD_SEL'.$counter.'_'.$partid.'" '. 'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n"; if ($$record{'resource.'.$partid.'.solved'} eq 'excused') { $line.='<option></option>'. @@ -1726,9 +1787,8 @@ sub gradeBox { #&mt('<td><b>Part:</b></td><td>[_1]</td><td><b>Points:</b></td><td>[_2]</td><td>or</td><td>[_3]</td>',$display_part,$radio,$line); $result .= - '<td><b>'.&mt('Part').':</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points').':</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'. - - $result.='</tr></table>'."\n"; + '<td>'.$display_part.'</td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'; + $result.=&Apache::loncommon::end_data_table_row(); $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n". '<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n". '<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'. @@ -1737,13 +1797,17 @@ sub gradeBox { $$record{'resource.'.$partid.'.tries'}.'" />'."\n". '<input type="hidden" name="aggtries'.$counter.'_'.$partid.'" value="'. $aggtries.'" />'."\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); foreach my $part_response_id (@part_response_id) { @@ -2038,7 +2102,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 ? '' : '<br />')); @@ -2105,9 +2174,11 @@ KEYWORDS } my $responsetype = $responseType->{$partid}->{$respid}; if (!exists($record{"resource.$partid.$respid.submission"})) { - $lastsubonly.="\n".'<div class="LC_grade_submission_part"><b>Part:</b> '. - $display_part.' <span class="LC_internal_info">( ID '.$respid. - ' )</span> '. + $lastsubonly.="\n".'<div class="LC_grade_submission_part">'. + '<b>'.&mt('Part: [_1]',$display_part).'</b>'. + ' <span class="LC_internal_info">'. + '('.&mt('Part ID: [_1]',$respid).')</b>'. + '</span> '. '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>'; next; } @@ -2127,10 +2198,9 @@ KEYWORDS {'one_time' => 1}); $similar="<hr /><h3><span class=\"LC_warning\">". - &mt('Essay is [_1]% similar to an essay by [_2] ([_3]:[_4]) in course [_5] (course id [_6]:[_7])', + &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, + &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')', $old_course_desc{'description'}, $old_course_desc{'num'}, $old_course_desc{'domain'}). @@ -2144,9 +2214,11 @@ KEYWORDS ($env{'form.lastSub'} eq 'hdgrade' && $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) { my $display_part=&get_display_part($partid,$symb); - $lastsubonly.='<div class="LC_grade_submission_part"><b>Part:</b> '. - $display_part.' <span class="LC_internal_info">( ID '.$respid. - ' )</span> '; + $lastsubonly.='<div class="LC_grade_submission_part">'. + '<b>'.&mt('Part: [_1]',$display_part).'</b>'. + ' <span class="LC_internal_info">'. + '('.&mt('Part ID: [_1]',$respid).')'. + '</b></span> '; my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record); if (@$files) { $lastsubonly.='<br /><span class="LC_warning">'.&mt('Like all files provided by users, this file may contain viruses').'</span><br />'; @@ -2154,7 +2226,7 @@ KEYWORDS foreach my $file (@$files) { $file_counter++; &Apache::lonnet::allowuploaded('/adm/grades',$file); - $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border=0"> '.$file.'</a>'; + $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" /> '.$file.'</a>'; } $lastsubonly.='<br />'; } @@ -2228,11 +2300,12 @@ KEYWORDS my @partlist; my @gradePartRespid; my @part_response_id = &flatten_responseType($responseType); - $request->print('<div class="LC_grade_assign">'. - - '<div class="LC_grade_assign_header">'. - &mt('Assign Grades').'</div>'. - '<div class="LC_grade_assign_body">'); + $request->print( + '<div class="LC_grade_assign">' + .'<div class="LC_grade_assign_header">' + .&mt('Assign Grades').'</div>' + ); + $request->print(&gradeBox_start()); # <div class="LC_grade_assign_body"> foreach my $part_response_id (@part_response_id) { my ($partid,$respid) = @{ $part_response_id }; my $part_resp = join('_',@{ $part_response_id }); @@ -2244,7 +2317,8 @@ KEYWORDS push(@gradePartRespid,$partid.'.'.$respid); $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record)); } - $request->print('</div></div>'); + $request->print(&gradeBox_end()); # </div> + $request->print('</div>'); $request->print('<div class="LC_grade_info_links">'); if ($perm{'vgr'}) { @@ -2289,7 +2363,7 @@ KEYWORDS '<option>7</option><option>10</option></select>'."\n"; my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1'); $ntstu =~ s/<option>$nsel</<option selected="selected">$nsel</; - $endform.=&mt('[quant,_1,student]',$ntstu); + $endform.=&mt('[_1]student(s)',$ntstu); $endform.=' <input type="button" value="'.&mt('Previous').'" '. 'onClick="javascript:checksubmit(this.form,\'Previous\');" target="_self" /> '."\n". '<input type="button" value="'.&mt('Next').'" '. @@ -2455,7 +2529,7 @@ sub processHandGrade { undef,$feedurl,undef, undef,undef,$showsymb, $restitle); - $request->print('<br />'.&mt('Sending message to [_1]:[_2]',$uname,$udom).': '. + $request->print('<br />'.&mt('Sending message to [_1]',$uname.':'.$udom).': '. $msgstatus); } if ($env{'form.collaborator'.$ctr}) { @@ -2586,7 +2660,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); @@ -2784,8 +2863,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('<br />'.&navmap_errormsg().'<br />'); + return; + } my @part_response_id = &flatten_responseType($responseType); foreach my $part_response_id (@part_response_id) { my ($part_id,$resp_id) = @{ $part_response_id }; @@ -3228,22 +3311,26 @@ sub viewgrades { '<input type="hidden" name="Status" value="'.$env{'stu_status'}.'" />'."\n". '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n"; - my $sectionClass; - my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section')); + my ($common_header,$specific_header); if ($env{'form.section'} eq 'all') { - $sectionClass=&mt('Class'); + $common_header = &mt('Assign Common Grade to Class'); + $specific_header = &mt('Assign Grade to Specific Students in Class'); } elsif ($env{'form.section'} eq 'none') { - $sectionClass=&mt('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'); } else { - $sectionClass=&mt('Students in Section(s) [_1]'); + my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section')); + $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); } - $result.= - '<h3>'. - &mt("Assign Common Grade to [_1]",$sectionClass,$section_display).'</h3>'; - $result.= &Apache::loncommon::start_data_table(); + $result.= '<h3>'.$common_header.'</h3>'.&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 = (); @@ -3287,7 +3374,7 @@ sub viewgrades { $result.= &Apache::loncommon::start_data_table_row()."\n". - '<td><b>'.&mt('Part').':</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points').':</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'. + '<td><b>'.&mt('Part:').'</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points:').'</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'. &Apache::loncommon::end_data_table_row()."\n"; $ctsparts++; } @@ -3298,13 +3385,16 @@ sub viewgrades { #table listing all the students in a section/class #header of table - $result.= '<h3>'.&mt('Assign Grade to Specific Students in ').$sectionClass, - $section_display.'</h3>'; - $result.= &Apache::loncommon::start_data_table(). - &Apache::loncommon::start_data_table_header_row(). - '<th>'.&mt('No.').'</th>'. - '<th>'.&nameUserString('header')."</th>\n"; - my (@parts) = sort(&getpartlist($symb)); + $result.= '<h3>'.$specific_header.'</h3>'. + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(). + '<th>'.&mt('No.').'</th>'. + '<th>'.&nameUserString('header')."</th>\n"; + 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) { @@ -3463,7 +3553,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}; @@ -3674,7 +3768,7 @@ sub split_part_type { # #--- Javascript to handle csv upload sub csvupload_javascript_reverse_associate { - my $error1=&mt('You need to specify the username or ID'); + my $error1=&mt('You need to specify the username or the student/employee ID'); my $error2=&mt('You need to specify at least one grading field'); return(<<ENDPICK); function verify(vf) { @@ -3714,7 +3808,7 @@ ENDPICK } sub csvupload_javascript_forward_associate { - my $error1=&mt('You need to specify the username or ID'); + my $error1=&mt('You need to specify the username or the student/employee ID'); my $error2=&mt('You need to specify at least one grading field'); return(<<ENDPICK); function verify(vf) { @@ -3795,8 +3889,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']); @@ -3896,8 +3996,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, @@ -4083,14 +4187,14 @@ sub csvuploadassign { $countdone++; } } - $request->print('<br /><span class="LC_info">'.&mt("Saved [_1] students",$countdone)."</span>\n"); + $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt("Saved scores for [quant,_1,student]",$countdone),$countdone==0)); if (@skipped) { - $request->print('<p><span class="LC_warning">'.&mt('Skipped Students').'</span></p>'); - foreach my $student (@skipped) { $request->print("$student<br />\n"); } + $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('No scores stored for the following username(s):'),1).'<br />'); + $request->print(join(', ',@skipped)); } if (@notallowed) { - $request->print('<p><span class="LC_error">'.&mt('Students Not Allowed to Modify').'</span></p>'); - foreach my $student (@notallowed) { $request->print("$student<br />\n"); } + $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('Modification of scores not allowed for the following username(s):'),1).'<br />'); + $request->print(join(', ',@notallowed)); } $request->print("<br />\n"); $request->print(&show_grading_menu_form($symb)); @@ -4133,7 +4237,12 @@ LISTJAVASCRIPT &mt('Manual Grading by Page or Sequence').'</span></h3>'; $result.='<form action="/adm/grades" method="post" name="displayPage">'."\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)/); @@ -4228,8 +4337,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; @@ -4288,6 +4403,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) { @@ -4386,11 +4506,13 @@ sub displayPage { } if (&canmodify($usec)) { + $studentTable.=&gradeBox_start(); foreach my $partid (@{$parts}) { $studentTable.=&gradeBox($request,$symbx,$uname,$udom,$question,$partid,\%record); $studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n"; $question++; } + $studentTable.=&gradeBox_end(); $prob++; } $studentTable.='</td></tr>'; @@ -4460,13 +4582,16 @@ sub displaySubByDates { my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/) : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/)); - $displaySub[0].='<b>'.&mt('Part:').'</b> '.$display_part.' '; - $displaySub[0].='<span class="LC_internal_info">('.&mt('ID').' '. - $responseId.')</span> <b>'; + $displaySub[0].='<span class="LC_nobreak"'; + $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>' + .' <span class="LC_internal_info">' + .'('.&mt('Part ID: [_1]',$responseId).')' + .'</span>' + .' <b>'; if ($$record{"$where.$partid.tries"} eq '') { - $displaySub[0].=&mt('Trial not counted'); + $displaySub[0].=&mt('Trial not counted'); } else { - $displaySub[0].=&mt('Trial [_1]', + $displaySub[0].=&mt('Trial: [_1]', $$record{"$where.$partid.tries"}); } my $responseType=($isTask ? 'Task' @@ -4477,7 +4602,8 @@ sub displaySubByDates { &get_order($partid,$responseId,$symb,$uname,$udom, $no_increment); } - $displaySub[0].='</b> '. + $displaySub[0].='</b></span>'; # /nobreak + $displaySub[0].=' '. &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom).'<br />'; } } @@ -4538,7 +4664,12 @@ 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) { @@ -4683,7 +4814,7 @@ sub updateGradeByPage { # #------------------------------------------------------------------- -#--------------------Scantron Grading----------------------------------- +#-------------------- Bubblesheet (Scantron) Grading ------------------- # #------ start of section for handling grading by page/sequence --------- @@ -4761,14 +4892,19 @@ sub defaultFormData { Return html dropdown of possible sequences to grade 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 sub getSequenceDropDown { - my ($symb)=@_; + my ($symb,$map_error)=@_; my $result='<select name="selectpage">'."\n"; - my ($titles,$symbx) = &getSymbMap(); + my ($titles,$symbx) = &getSymbMap($map_error); + if (ref($map_error)) { + return if ($$map_error); + } my ($curpage)=&Apache::lonnet::decode_symb($symb); my $ctr=0; foreach (@$titles) { @@ -5034,7 +5170,12 @@ sub scantron_selectphase { my ($r,$file2grade) = @_; my ($symb)=&get_symb($r); if (!$symb) {return '';} - my $sequence_selector=&getSequenceDropDown($symb); + my $map_error; + my $sequence_selector=&getSequenceDropDown($symb,\$map_error); + if ($map_error) { + $r->print('<br />'.&navmap_errormsg().'<br />'); + return; + } my $default_form_data=&defaultFormData($symb); my $grading_menu_button=&show_grading_menu_form($symb); my $file_selector=&scantron_uploads($file2grade); @@ -5062,10 +5203,10 @@ sub scantron_selectphase { <td> '.&mt('Sequence to grade:').' </td><td> '.$sequence_selector.' </td> '.&Apache::loncommon::end_data_table_row().' '.&Apache::loncommon::start_data_table_row().' - <td> '.&mt('Filename of scoring office file:').' </td><td> '.$file_selector.' </td> + <td> '.&mt('Filename of bubblesheet data file:').' </td><td> '.$file_selector.' </td> '.&Apache::loncommon::end_data_table_row().' '.&Apache::loncommon::start_data_table_row().' - <td> '.&mt('Format of data file:').' </td><td> '.$format_selector.' </td> + <td> '.&mt('Format of bubblesheet data file:').' </td><td> '.$format_selector.' </td> '.&Apache::loncommon::end_data_table_row().' '.&Apache::loncommon::start_data_table_row().' <td> '.&mt('Saved CODEs to validate against:').' </td><td> '.$CODE_selector.' </td> @@ -5083,7 +5224,7 @@ sub scantron_selectphase { '.&Apache::loncommon::end_data_table_row().' '.&Apache::loncommon::start_data_table_row().' <td colspan="2"> - <input type="submit" value="'.&mt('Grading: Validate Scantron Records').'" /> + <input type="submit" value="'.&mt('Grading: Validate Bubblesheet Records').'" /> </td> '.&Apache::loncommon::end_data_table_row().' '.&Apache::loncommon::end_data_table().' @@ -5102,7 +5243,7 @@ sub scantron_selectphase { '.&Apache::loncommon::start_data_table('LC_scantron_action').' '.&Apache::loncommon::start_data_table_header_row().' <th> - '.&mt('Specify a Scantron data file to upload.').' + '.&mt('Specify a bubblesheet data file to upload.').' </th> '.&Apache::loncommon::end_data_table_header_row().' '.&Apache::loncommon::start_data_table_row().' @@ -5129,7 +5270,7 @@ sub scantron_selectphase { <input name="command" value="scantronupload_save" type="hidden" /> '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').' <br /> - <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Scantron Data').'" /> + <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" /> </form> '); @@ -5171,7 +5312,7 @@ sub scantron_selectphase { &Apache::loncommon::start_data_table('LC_scantron_action')."\n". &Apache::loncommon::start_data_table_header_row()."\n". '<th colspan="2"> - '.&mt('Review scantron data and submissions for a previously graded folder/sequence')."\n". + '.&mt('Review bubblesheet data and submissions for a previously graded folder/sequence')."\n". '</th>'."\n". &Apache::loncommon::end_data_table_header_row()."\n". &Apache::loncommon::start_data_table_row()."\n". @@ -5193,7 +5334,7 @@ sub scantron_selectphase { &Apache::loncommon::start_data_table_row()."\n". '<td colspan="2">'."\n". '<input type="hidden" name="command" value="checksubmissions" />'."\n". - '<input type="submit" value="'.&mt('Review Scantron Data and Submission Records').'" />'."\n". + '<input type="submit" value="'.&mt('Review Bubblesheet Data and Submission Records').'" />'."\n". '</td>'."\n". &Apache::loncommon::end_data_table_row()."\n". &Apache::loncommon::end_data_table()."\n". @@ -5234,7 +5375,7 @@ sub scantron_selectphase { CODEstart - (only matter if a CODE exists) column in the line where the CODE starts CODElength - length of the CODE - IDstart - column where the student/employee ID number starts + IDstart - column where the student/employee ID starts IDlength - length of the student/employee ID info Qstart - column where the information from the bubbled 'questions' start @@ -5334,7 +5475,7 @@ sub username_to_idmap { $whichline - line number of the passed in scanline $field - type of change to process (either - 'ID' -> correct the student/employee ID number + 'ID' -> correct the student/employee ID 'CODE' -> correct the CODE 'answer' -> fixup the submitted answers) @@ -6208,7 +6349,12 @@ sub scantron_validate_file { $r->print('<p>'.&mt('Gathering necessary information.').'</p>');$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); @@ -6244,7 +6390,7 @@ sub scantron_validate_file { '<input type="radio" name="verifyrecord" value="0" checked="checked" />'.&mt('No'). '</label></span><br />'. &mt('Grading will take longer if you use verification.').'<br />'. - &mt("Alternatively, the 'Review scantron data' utility (see grading menu) can be used for all students after grading is complete.").'<br /><br />'. + &mt("Alternatively, the 'Review bubblesheet data' utility (see grading menu) can be used for all students after grading is complete.").'<br /><br />'. '<input type="submit" name="submit" value="'.&mt('Start Grading').'" />'. '<input type="hidden" name="command" value="scantron_process" />'."\n"); } else { @@ -6620,6 +6766,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'}); @@ -6651,8 +6801,13 @@ sub scantron_validate_ID { #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_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++) { @@ -6770,10 +6925,10 @@ sub scantron_get_correction { if ($closest > 0) { foreach my $testcode (@{$closest}) { my $checked=''; - if (!$i) { $checked=' checked="checked" '; } + if (!$i) { $checked=' checked="checked"'; } $r->print(" <label> - <input type='radio' name='scantron_CODE_resolution' value='use_closest_$i' $checked /> + <input type='radio' name='scantron_CODE_resolution' value='use_closest_$i'$checked /> ".&mt("Use the similar CODE [_1] instead.", "<b><tt>".$testcode."</tt></b>")." </label> @@ -6784,10 +6939,10 @@ sub scantron_get_correction { } } if ($$scan_record{'scantron.CODE'}=~/\S/ ) { - my $checked; if (!$i) { $checked=' checked="checked" '; } + my $checked; if (!$i) { $checked=' checked="checked"'; } $r->print(" <label> - <input type='radio' name='scantron_CODE_resolution' value='use_unfound' $checked /> + <input type='radio' name='scantron_CODE_resolution' value='use_unfound'$checked /> ".&mt("Use the CODE [_1] that is was on the paper, ignoring the error.", "<b><tt>".$$scan_record{'scantron.CODE'}."</tt></b>")." </label>"); @@ -7011,7 +7166,7 @@ sub prompt_for_corrections { ($responsetype_per_response{$question-1} eq 'imageresponse') || ($responsetype_per_response{$question-1} eq 'reactionresponse') || ($responsetype_per_response{$question-1} eq 'organicresponse')) { - $r->print(&mt("Although this particular question type requires handgrading, the instructions for this question in the exam directed students to leave [quant,_1,line] blank on their scantron sheets.",$lines).'<br /><br />'.&mt('A non-zero score can be assigned to the student during scantron 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 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 { $r->print(&mt("Select at most one bubble in a single line and select 'No Bubble' in all the other lines. ")."<br />"); } @@ -7205,7 +7360,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++) { @@ -7259,7 +7419,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); @@ -7277,6 +7442,7 @@ sub scantron_validate_doublebubble { sub scantron_get_maxbubble { + my ($nav_error) = @_; if (defined($env{'form.scantron_maxbubble'}) && $env{'form.scantron_maxbubble'}) { &restore_bubble_lines(); @@ -7287,6 +7453,11 @@ 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; + } + } my $map=$navmap->getResourceByUrl($sequence); my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); @@ -7376,7 +7547,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); @@ -7436,13 +7611,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); @@ -7454,6 +7640,10 @@ sub scantron_process_students { } } } + if ($resource_error) { + $r->print(&navmap_errormsg()); + return ''; + } my ($uname,$udom); my $result= <<SCANTRONFORM; @@ -7468,8 +7658,8 @@ 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,'Scantron Status', - 'Scantron Progress',$count, + my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Bubblesheet Status', + 'Bubblesheet Progress',$count, 'inline',undef,'scantronupload'); &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state, 'Processing first student'); @@ -7478,7 +7668,12 @@ SCANTRONFORM my $i=-1; my $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 ''; + } # If an ssi failed in scantron_get_maxbubble, put an error message out to # the user and return. @@ -7519,9 +7714,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) = @@ -7532,6 +7733,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); @@ -7694,39 +7901,68 @@ sub grade_student_bubbles { sub scantron_upload_scantron_data { my ($r)=@_; - $r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'})); + my $dom = $env{'request.role.domain'}; + my $domdesc = &Apache::lonnet::domain($dom,'description'); + $r->print(&Apache::loncommon::coursebrowser_javascript($dom)); my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid', 'domainid', - 'coursename'); - my $domsel=&Apache::loncommon::select_dom_form($env{'request.role.domain'}, - 'domainid'); + 'coursename',$dom); + my $syllabuslink = '<a href="javascript:ToSyllabus();">'.&mt('Syllabus').'</a>'. + (' 'x2).&mt('(shows course personnel)'); my $default_form_data=&defaultFormData(&get_symb($r,1)); + 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(' <script type="text/javascript" language="javascript"> function checkUpload(formname) { if (formname.upfile.value == "") { - alert("'.&mt('Please use the browse button to select a file from your local directory.').'"); + alert("'.$nofile_alert.'"); return false; } + if (formname.courseid.value == "") { + alert("'.$nocourseid_alert.'"); + return false; + } formname.submit(); } + + function ToSyllabus() { + var cdom = '."'$dom'".'; + var cnum = document.rules.courseid.value; + if (cdom == "" || cdom == null) { + return; + } + if (cnum == "" || cnum == null) { + return; + } + syllwin=window.open("/public/"+cdom+"/"+cnum+"/syllabus","LONCAPASyllabus", + "height=350,width=350,scrollbars=yes,menubar=no"); + return; + } + </script> +<h3>'.&mt('Send scanned bubblesheet data to a course').'</h3> + <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post"> -'.$default_form_data.' -<table> -<tr><td>'.$select_link.' </td></tr> -<tr><td>'.&mt('Course ID:').' </td> - <td><input name="courseid" type="text" /> </td></tr> -<tr><td>'.&mt('Course Name:').' </td> - <td><input name="coursename" type="text" /> </td></tr> -<tr><td>'.&mt('Domain:').' </td> - <td>'.$domsel.' </td></tr> -<tr><td>'.&mt('File to upload:').'</td> - <td><input type="file" name="upfile" size="50" /></td></tr> -</table> +'.$default_form_data. + &Apache::lonhtmlcommon::start_pick_box(). + &Apache::lonhtmlcommon::row_title(&mt('Course ID')). + '<input name="courseid" type="text" size="30" />'.$select_link. + &Apache::lonhtmlcommon::row_closure(). + &Apache::lonhtmlcommon::row_title(&mt('Course Name')). + '<input name="coursename" type="text" size="30" />'.$syllabuslink. + &Apache::lonhtmlcommon::row_closure(). + &Apache::lonhtmlcommon::row_title(&mt('Domain')). + '<input name="domainid" type="hidden" />'.$domdesc. + &Apache::lonhtmlcommon::row_closure(). + &Apache::lonhtmlcommon::row_title(&mt('File to upload')). + '<input type="file" name="upfile" size="50" />'. + &Apache::lonhtmlcommon::row_closure(1). + &Apache::lonhtmlcommon::end_pick_box().'<br /> + <input name="command" value="scantronupload_save" type="hidden" /> -<input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Scantron Data').'" /> +<input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" /> </form> '); return ''; @@ -7744,7 +7980,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.")."<br />"); + $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."<br />"); if ($symb) { $r->print(&show_grading_menu_form($symb)); } else { @@ -7753,36 +7989,25 @@ sub scantron_upload_scantron_data_save { return ''; } my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'}); - $r->print(&mt("Doing upload to [_1]",$coursedata{'description'})." <br />"); - my $fname=$env{'form.upfile.filename'}; - #FIXME - #copied from lonnet::userfileupload() - #make that function able to target a specified course - # Replace Windows backslashes by forward slashes - $fname=~s/\\/\//g; - # Get rid of everything but the actual filename - $fname=~s/^.*\/([^\/]+)$/$1/; - # Replace spaces by underscores - $fname=~s/\s+/\_/g; - # Replace all other weird characters by nothing - $fname=~s/[^\w\.\-]//g; - # See if there is anything left - unless ($fname) { return 'error: no uploaded file'; } - my $uploadedfile=$fname; - $fname='scantron_orig_'.$fname; + my $uploadedfile; + $r->print('<h3>'.&mt("Uploading file to [_1]",$coursedata{'description'}).'</h3>'); if (length($env{'form.upfile'}) < 2) { - $r->print(&mt("<span class=\"LC_error\">Error:</span> The file you attempted to upload, [_1] contained no information. Please check that you entered the correct filename.",'<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</span>")); + $r->print(&mt('[_1]Error:[_2] The file you attempted to upload, [_3] contained no information. Please check that you entered the correct filename.','<span class="LC_error">','</span>','<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>')); } else { - my $result=&Apache::lonnet::finishuserfileupload($env{'form.courseid'},$env{'form.domainid'},'upfile',$fname); - if ($result =~ m|^/uploaded/|) { - $r->print(&mt("<span class=\"LC_success\">Success:</span> Successfully uploaded [_1] bytes of data into location [_2]", - (length($env{'form.upfile'})-1), - '<span class="LC_filename">'.$result."</span>")); + my $result = + &Apache::lonnet::userfileupload('upfile','','scantron','','','', + $env{'form.courseid'},$env{'form.domainid'}); + if ($result =~ m{^/uploaded/}) { + $r->print(&mt('[_1]Success:[_2] Successfully uploaded [_3] bytes of data into location: [_4]', + '<span class="LC_success">','</span>',(length($env{'form.upfile'})-1), + '<span class="LC_filename">'.$result.'</span>')); + ($uploadedfile) = ($result =~ m{/([^/]+)$}); + $r->print(&validate_uploaded_scantron_file($env{'form.domainid'}, + $env{'form.courseid'},$uploadedfile)); } else { - $r->print(&mt("<span class=\"LC_error\">Error:</span> An error ([_1]) occurred when attempting to upload the file, [_2]", - $result, - '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</span>")); - + $r->print(&mt('[_1]Error:[_2] An error ([_3]) occurred when attempting to upload the file, [_4]', + '<span class="LC_error">','</span>',$result, + '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>')); } } if ($symb) { @@ -7793,6 +8018,92 @@ sub scantron_upload_scantron_data_save { return ''; } +sub validate_uploaded_scantron_file { + my ($cdom,$cname,$fname) = @_; + my $scanlines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.$fname); + my @lines; + if ($scanlines ne '-1') { + @lines=split("\n",$scanlines,-1); + } + my $output; + if (@lines) { + my (%counts,$max_match_format); + my ($max_match_count,$max_match_pct) = (0,0); + my $classlist = &Apache::loncoursedata::get_classlist($cdom,$cname); + my %idmap = &username_to_idmap($classlist); + foreach my $key (keys(%idmap)) { + my $lckey = lc($key); + $idmap{$lckey} = $idmap{$key}; + } + my %unique_formats; + my @formatlines = &get_scantronformat_file(); + foreach my $line (@formatlines) { + chomp($line); + my @config = split(/:/,$line); + my $idstart = $config[5]; + my $idlength = $config[6]; + if (($idstart ne '') && ($idlength > 0)) { + if (ref($unique_formats{$idstart.':'.$idlength}) eq 'ARRAY') { + push(@{$unique_formats{$idstart.':'.$idlength}},$config[0].':'.$config[1]); + } else { + $unique_formats{$idstart.':'.$idlength} = [$config[0].':'.$config[1]]; + } + } + } + foreach my $key (keys(%unique_formats)) { + my ($idstart,$idlength) = split(':',$key); + %{$counts{$key}} = ( + 'found' => 0, + 'total' => 0, + ); + foreach my $line (@lines) { + next if ($line =~ /^#/); + next if ($line =~ /^[\s\cz]*$/); + my $id = substr($line,$idstart-1,$idlength); + $id = lc($id); + if (exists($idmap{$id})) { + $counts{$key}{'found'} ++; + } + $counts{$key}{'total'} ++; + } + if ($counts{$key}{'total'}) { + my $percent_match = (100*$counts{$key}{'found'})/($counts{$key}{'total'}); + if (($max_match_format eq '') || ($percent_match > $max_match_pct)) { + $max_match_pct = $percent_match; + $max_match_format = $key; + $max_match_count = $counts{$key}{'total'}; + } + } + } + if (ref($unique_formats{$max_match_format}) eq 'ARRAY') { + my $format_descs; + my $numwithformat = @{$unique_formats{$max_match_format}}; + for (my $i=0; $i<$numwithformat; $i++) { + my ($name,$desc) = split(':',$unique_formats{$max_match_format}[$i]); + if ($i<$numwithformat-2) { + $format_descs .= '"<i>'.$desc.'</i>", '; + } elsif ($i==$numwithformat-2) { + $format_descs .= '"<i>'.$desc.'</i>" '.&mt('and').' '; + } elsif ($i==$numwithformat-1) { + $format_descs .= '"<i>'.$desc.'</i>"'; + } + } + my $showpct = sprintf("%.0f",$max_match_pct).'%'; + $output .= '<br />'.&mt('Comparison of student IDs in the uploaded file with the course roster found matches for [_1] of the [_2] entries in the file (for the format defined for [_3]).','<b>'.$showpct.'</b>','<b>'.$max_match_count.'</b>',$format_descs). + '<br />'.&mt('A low percentage of matches results from one of the following:').'<ul>'. + '<li>'.&mt('The file was uploaded to the wrong course').'</li>'. + '<li>'.&mt('The data are not in the format expected for the domain: [_1]', + '<i>'.$cdom.'</i>').'</li>'. + '<li>'.&mt('Students did not bubble their IDs, or mis-bubbled them').'</li>'. + '<li>'.&mt('The course roster is not up to date').'</li>'. + '</ul>'; + } + } else { + $output = '<span class="LC_warning">'.&mt('Uploaded file contained no data').'</span>'; + } + return $output; +} + sub valid_file { my ($requested_file)=@_; foreach my $filename (sort(&scantron_filenames())) { @@ -7858,6 +8169,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); @@ -7872,12 +8187,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'); @@ -7950,14 +8269,14 @@ sub checkscantron_results { if ($scandata{$pid} eq $record{$pid}) { my $css_class = ($passed % 2)?'LC_odd_row':'LC_even_row'; $okstudents .= '<tr class="'.$css_class.'">'. -'<td>'.&mt('Scantron').'</td><td>'.$showscandata.'</td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n". +'<td>'.&mt('Bubblesheet').'</td><td>'.$showscandata.'</td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n". '</tr>'."\n". '<tr class="'.$css_class.'">'."\n". '<td>Submissions</td><td>'.$showrecord.'</td></tr>'."\n"; $passed ++; } else { my $css_class = ($failed % 2)?'LC_odd_row':'LC_even_row'; - $badstudents .= '<tr class="'.$css_class.'"><td>'.&mt('Scantron').'</td><td><span class="LC_nobreak">'.$scandata{$pid}.'</span></td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n". + $badstudents .= '<tr class="'.$css_class.'"><td>'.&mt('Bubblesheet').'</td><td><span class="LC_nobreak">'.$scandata{$pid}.'</span></td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n". '</tr>'."\n". '<tr class="'.$css_class.'">'."\n". '<td>Submissions</td><td><span class="LC_nobreak">'.$record{$pid}.'</span></td>'."\n". @@ -7968,10 +8287,10 @@ sub checkscantron_results { } } } - $r->print('<p>'.&mt('Comparison of scantron data (including corrections) with corresponding submission records (most recent submission) for <b>[quant,_1,student]</b> ([_2] scantron lines/student).',$numstudents,$env{'form.scantron_maxbubble'}).'</p>'); + $r->print('<p>'.&mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for <b>[quant,_1,student]</b> ([_2] scantron lines/student).',$numstudents,$env{'form.scantron_maxbubble'}).'</p>'); $r->print('<p>'.&mt('Exact matches for <b>[quant,_1,student]</b>.',$passed).'<br />'.&mt('Discrepancies detected for <b>[quant,_1,student]</b>.',$failed).'</p>'); if ($passed) { - $r->print(&mt('Students with exact correspondence between scantron 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". &Apache::loncommon::start_data_table_header_row()."\n". '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'. @@ -7980,14 +8299,14 @@ sub checkscantron_results { &Apache::loncommon::end_data_table().'<br />'); } if ($failed) { - $r->print(&mt('Students with differences between scantron data and submissions are as follows:').'<br /><br />'); + $r->print(&mt('Students with differences between bubblesheet data and submissions are as follows:').'<br /><br />'); $r->print(&Apache::loncommon::start_data_table()."\n". &Apache::loncommon::start_data_table_header_row()."\n". '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'. &Apache::loncommon::end_data_table_header_row()."\n". $badstudents."\n". &Apache::loncommon::end_data_table()).'<br />'. - &mt('Differences can occur if submissions were modified using manual grading after a scantron grading pass.').'<br />'.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original scantron sheets.'); + &mt('Differences can occur if submissions were modified using manual grading after a bubblesheet grading pass.').'<br />'.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original bubblesheets.'); } $r->print('</form><br />'.$grading_menu_button); return; @@ -8202,7 +8521,7 @@ sub grading_menu { icon => 'addClickerInfoFile.png', linktitle => 'Specify a file containing the clicker information for this resource.' }, - { linktext => 'Grade/Manage/Review Scantron Forms', + { linktext => 'Grade/Manage/Review Bubblesheet Forms', url => $url4, permission => 'F', icon => 'stat.png', @@ -8228,7 +8547,7 @@ sub grading_menu { $Str .= Apache::lonhtmlcommon::generate_menu(@menu); #$menudata->{'jscript'} - $Str .='<hr /><input type="button" value="'.&mt('Verify Receipt').'" '. + $Str .='<hr /><input type="button" value="'.&mt('Verify Receipt No.').'" '. ' onClick="javascript:checkChoice(document.forms.gradingMenu,\'5\',\'verify\')" '. ' /> '. &Apache::lonnet::recprefix($env{'request.course.id'}). @@ -8548,7 +8867,7 @@ sub process_clicker { my %checked; foreach my $gradingmechanism ('attendance','personnel','specific','given') { if ($env{'form.gradingmechanism'} eq $gradingmechanism) { - $checked{$gradingmechanism}="checked='checked'"; + $checked{$gradingmechanism}=' checked="checked"'; } } @@ -8612,11 +8931,11 @@ function sanitycheck() { <input type="hidden" name="saveState" value="$env{'form.saveState'}" /> <input type="file" name="upfile" size="50" /> <br /><label>$type: $selectform</label> -<br /><label><input type="radio" name="gradingmechanism" value="attendance" $checked{'attendance'} onClick="sanitycheck()" />$attendance </label> -<br /><label><input type="radio" name="gradingmechanism" value="personnel" $checked{'personnel'} onClick="sanitycheck()" />$personnel</label> -<br /><label><input type="radio" name="gradingmechanism" value="specific" $checked{'specific'} onClick="sanitycheck()" />$specific </label> +<br /><label><input type="radio" name="gradingmechanism" value="attendance"$checked{'attendance'} onClick="sanitycheck()" />$attendance </label> +<br /><label><input type="radio" name="gradingmechanism" value="personnel"$checked{'personnel'} onClick="sanitycheck()" />$personnel</label> +<br /><label><input type="radio" name="gradingmechanism" value="specific"$checked{'specific'} onClick="sanitycheck()" />$specific </label> <input type="text" name="specificid" value="$env{'form.specificid'}" size="20" /> -<br /><label><input type="radio" name="gradingmechanism" value="given" $checked{'given'} onClick="sanitycheck()" />$given </label> +<br /><label><input type="radio" name="gradingmechanism" value="given"$checked{'given'} onClick="sanitycheck()" />$given </label> <br /> <input type="text" name="givenanswer" size="50" /> <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" /> @@ -8871,7 +9190,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 @@ -8935,13 +9258,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; @@ -8975,6 +9298,13 @@ ENDHEADER return $result.&show_grading_menu_form($symb); } +sub navmap_errormsg { + return '<div class="LC_error">'. + &mt('An error occurred retrieving information about resources in the course.').'<br />'. + &mt('It is recommended that you [_1]re-initialize the course[_2] and then return to this grading page..','<a href="/adm/roles?selectrole=1&newrole='.$env{'request.role'}.'">','</a>'). + '</div>'; +} + sub handler { my $request=$_[0]; &reset_caches(); @@ -9095,7 +9425,7 @@ sub handler { } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) { $request->print(&checkscantron_results($request)); } elsif ($command) { - $request->print("Access Denied ($command)"); + $request->print('<p class="LC_error">'.&mt('Access Denied ([_1])',$command).'</p>'); } } if ($ssi_error) { @@ -9200,6 +9530,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() @@ -9265,6 +9602,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 <div></div> with a link to re-initialize the course. + Should be called whenever the request to instantiate a navmap object fails. + =back =cut