--- loncom/homework/grades.pm 2008/12/31 20:41:35 1.545
+++ loncom/homework/grades.pm 2011/09/17 19:01:20 1.650
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.545 2008/12/31 20:41:35 raeburn Exp $
+# $Id: grades.pm,v 1.650 2011/09/17 19:01:20 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -40,9 +40,11 @@ use Apache::lonhomework;
use Apache::lonpickcode;
use Apache::loncoursedata;
use Apache::lonmsg();
-use Apache::Constants qw(:common);
+use Apache::Constants qw(:common :http);
use Apache::lonlocal;
use Apache::lonenc;
+use Apache::lonstathelpers;
+use Apache::lonquickgrades;
use String::Similarity;
use LONCAPA;
@@ -96,10 +98,19 @@ sub ssi_print_error {
#
# --- Retrieve the parts from the metadata file.---
+# Returns an array of everything that the resources stores away
+#
+
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();
@@ -114,21 +125,6 @@ sub getpartlist {
return @stores;
}
-# --- 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("Unable to handle ambiguous references:$url:.");
- return ();
- }
- }
- &Apache::lonenc::check_decrypt(\$symb);
- return ($symb);
-}
-
#--- Format fullname, username:domain if different for display
#--- Use anywhere where the student names are listed
sub nameUserString {
@@ -143,11 +139,22 @@ sub nameUserString {
#--- Get the partlist and the response type for a given problem. ---
#--- Indicate if a response type is coded handgraded or not. ---
+#--- Sets response_error pointer to "1" if navmaps object broken ---
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);
+ unless (ref($res)) {
+ $$response_error = 1;
+ return;
+ }
my $partlist = $res->parts();
my %vPart =
map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart'));
@@ -183,50 +190,14 @@ sub get_display_part {
my ($partID,$symb)=@_;
my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb);
if (defined($display) and $display ne '') {
- $display.= " (id $partID)";
+ $display.= ' ('
+ .&mt('Part ID: [_1]',$partID).')';
} else {
$display=$partID;
}
return $display;
}
-#--- Show resource title
-#--- and parts and response type
-sub showResourceInfo {
- my ($symb,$probTitle,$checkboxes) = @_;
- my $col=3;
- if ($checkboxes) { $col=4; }
- my $result = '
'.&mt('Current Resource').': '.$probTitle.'
'."\n";
- $result .='
';
- my ($partlist,$handgrade,$responseType) = &response_type($symb);
- 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.='
';
- if ($checkboxes) {
- if (exists($partsseen{$partID})) {
- $result.="
'.
- &mt('The above receipt matches the following [numerate,_1,student].',$matches).
+ &mt('The above receipt number matches the following [quant,_1,student].',$matches).
'
'.
$header.
$contents.
&Apache::loncommon::end_data_table()."\n";
}
- return $string.&show_grading_menu_form($symb);
+ return $string;
}
#--- This is called by a number of programs.
@@ -771,32 +827,25 @@ sub verifyreceipt {
#--- Also called directly when one clicks on the subm button
# on the problem page.
sub listStudents {
- my ($request) = shift;
+ my ($request,$symb,$submitonly) = @_;
- my ($symb) = &get_symb($request);
my $cdom = $env{"course.$env{'request.course.id'}.domain"};
my $cnum = $env{"course.$env{'request.course.id'}.num"};
my $getsec = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
my $getgroup = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
- my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
- my $viewgrade = $env{'form.showgrading'} eq 'yes' ? &mt('View/Grade/Regrade') : &mt('View');
- $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ?
- &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};
-
- my $result='
'.$viewgrade.
- &mt(' Submissions for a Student or a Group of Students')
- .'
';
-
- 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);
- $request->print(<
+ unless ($submitonly) {
+ $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
+ }
+
+ my $result='';
+ my $res_error;
+ my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
+
+ 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(&Apache::lonhtmlcommon::scripttag(<
LISTJAVASCRIPT
&commonJSfunctions($request);
$request->print($result);
- my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked="checked"' : '';
- my $checklastsub = $checkhdgrade eq '' ? 'checked="checked"' : '';
my $gradeTable=''."\n";
+ ''."\n";
if ($ctr == 0) {
my $num_students=(scalar(keys(%$fullname)));
if ($num_students eq 0) {
@@ -1034,7 +1084,6 @@ LISTJAVASCRIPT
} elsif ($ctr == 1) {
$gradeTable =~ s/type="checkbox"/type="checkbox" checked="checked"/;
}
- $gradeTable.=&show_grading_menu_form($symb);
$request->print($gradeTable);
return '';
}
@@ -1043,7 +1092,7 @@ LISTJAVASCRIPT
sub check_script {
my ($form, $type)=@_;
- my $chkallscript=''."\n";
+'."\n");
return $chkallscript;
}
@@ -1088,7 +1137,7 @@ sub check_buttons {
# Displays the submissions for one student or a group of students
sub processGroup {
- my ($request) = shift;
+ my ($request,$symb) = @_;
my $ctr = 0;
my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');
my $total = scalar(@stuchecked)-1;
@@ -1098,7 +1147,7 @@ sub processGroup {
$env{'form.student'} = $uname;
$env{'form.userdom'} = $udom;
$env{'form.fullname'} = $fullname;
- &submission($request,$ctr,$total);
+ &submission($request,$ctr,$total,$symb);
$ctr++;
}
return '';
@@ -1113,8 +1162,7 @@ sub processGroup {
sub sub_page_js {
my $request = shift;
my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = ');
- $request->print(<
+ $request->print(&Apache::lonhtmlcommon::scripttag(<
SUBJAVASCRIPT
}
@@ -1292,8 +1332,8 @@ sub sub_page_kw_js {
my $iconpath = $request->dir_config('lonIconsURL');
&commonJSfunctions($request);
- my $inner_js_msg_central=<
+ my $inner_js_msg_central= (<
function checkInput() {
opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value);
var nmsg = opener.document.SCORE.savemsgN.value;
@@ -1330,11 +1370,11 @@ sub sub_page_kw_js {
self.close()
}
-
+
INNERJS
- my $inner_js_highlight_central=<
+ my $inner_js_highlight_central= (<
function updateChoice(flag) {
opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);
opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);
@@ -1369,8 +1409,7 @@ INNERJS
my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
$docopen=~s/^document\.//;
my $alertmsg = &mt('Please select a word or group of words from document and then click this link.');
- $request->print(<
+ $request->print(&Apache::lonhtmlcommon::scripttag(<");
pDoc.write("
Compose Message for \"+fullname+\"<\\/span><\\/h3>
");
pDoc.write("<\\/form>");
pDoc.write('$end_page_msg_central');
pDoc.close();
@@ -1573,8 +1612,8 @@ INNERJS
hDoc.write(" ".&show_grading_menu_form($symb));
+ $r->print(" ");
return '';
}
@@ -6122,8 +6279,7 @@ SCANTRONFORM
=cut
sub scantron_validate_file {
- my ($r) = @_;
- my ($symb)=&get_symb($r);
+ my ($r,$symb) = @_;
if (!$symb) {return '';}
my $default_form_data=&defaultFormData($symb);
@@ -6150,7 +6306,13 @@ 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 %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;
$r->print($result);
@@ -6176,7 +6338,7 @@ sub scantron_validate_file {
}
}
if (!$stop) {
- my $warning=&scantron_warning_screen('Start Grading');
+ my $warning=&scantron_warning_screen('Start Grading',$symb);
$r->print(&mt('Validation process complete.').' '.
$warning.
&mt('Perform verification for each student after storage of submissions?').
@@ -6186,7 +6348,7 @@ sub scantron_validate_file {
''.&mt('No').
' '.
&mt('Grading will take longer if you use verification.').' '.
- &mt("Alternatively, the 'Review scantron data' utility (see grading menu) can be used for all students after grading is complete.").'
'.
+ &mt('Otherwise, Grade/Manage/Review Bubblesheets [_1] Review bubblesheet data can be used once grading is complete.','»').'
".&mt("Or click the 'Grading Menu' button to start over.")."
");
+ $r->print('
'.&mt('Or return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','','').'
');
} else {
if ($validate_phases[$currentphase] eq 'doublebubble' || $validate_phases[$currentphase] eq 'missingbubbles') {
$r->print('');
@@ -6210,7 +6372,7 @@ sub scantron_validate_file {
$r->print(" ".&mt("this scanline saving it for later."));
}
}
- $r->print(" ".&show_grading_menu_form($symb));
+ $r->print(" ");
return '';
}
@@ -6562,6 +6724,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'});
@@ -6593,8 +6759,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,\%scantron_config); # 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++) {
@@ -6712,10 +6883,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("
@@ -6726,18 +6897,17 @@ 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("
");
$r->print("\n ");
}
- $r->print(<
+ $r->print(&Apache::lonhtmlcommon::scripttag(<
ENDSCRIPT
my $href="/adm/pickcode?".
"form=".&escape("scantronupload").
@@ -6760,7 +6929,7 @@ ENDSCRIPT
".&mt("[_1]Select[_2] a CODE from the list of all CODEs and use it.",
"","")."
- ".&mt("Selected CODE is [_1]",""));
+ ".&mt("Selected CODE is [_1]",''));
$r->print("\n ");
}
$r->print("
@@ -6817,8 +6986,7 @@ sub verify_bubbles_checked {
my (@ansnums) = @_;
my $ansnumstr = join('","',@ansnums);
my $warning = &mt("A bubble or 'No bubble' selection has not been made for one or more lines.");
- my $output = (<
+ my $output = &Apache::lonhtmlcommon::scripttag((<
ENDSCRIPT
return $output;
}
@@ -6953,7 +7120,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).'
'.&mt('A non-zero score can be assigned to the student during scantron grading by selecting a bubble in at least one line.').' '.&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.').' '.&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.").'
');
+ $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).'
'.&mt('A non-zero score can be assigned to the student during bubblesheet grading by selecting a bubble in at least one line.').' '.&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.').' '.&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.").'
');
} else {
$r->print(&mt("Select at most one bubble in a single line and select 'No Bubble' in all the other lines. ")." ");
}
@@ -6993,7 +7160,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().
@@ -7147,7 +7326,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,\%scantron_config); # 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++) {
@@ -7201,7 +7385,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,\%scantron_config); # 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);
@@ -7219,6 +7408,7 @@ sub scantron_validate_doublebubble {
sub scantron_get_maxbubble {
+ my ($nav_error,$scantron_config) = @_;
if (defined($env{'form.scantron_maxbubble'}) &&
$env{'form.scantron_maxbubble'}) {
&restore_bubble_lines();
@@ -7229,24 +7419,31 @@ 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);
+ my $bubbles_per_row = &bubblesheet_bubbles_per_row($scantron_config);
&Apache::lonxml::clear_problem_counter();
- my $uname = $env{'form.student'};
- my $udom = $env{'form.userdom'};
+ my $uname = $env{'user.name'};
+ my $udom = $env{'user.domain'};
my $cid = $env{'request.course.id'};
my $total_lines = 0;
%bubble_lines_per_response = ();
%first_bubble_line = ();
%subdivided_bubble_lines = ();
%responsetype_per_response = ();
-
+
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,undef,$bubbles_per_row);
if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) {
foreach my $part_id (@{$parts}) {
my $lines;
@@ -7275,9 +7472,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++) {
@@ -7288,7 +7486,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;
@@ -7301,7 +7499,7 @@ sub scantron_get_maxbubble {
}
}
}
- &Apache::lonnet::delenv('scantron\.');
+ &Apache::lonnet::delenv('scantron.');
&save_bubble_lines();
$env{'form.scantron_maxbubble'} =
@@ -7309,31 +7507,16 @@ sub scantron_get_maxbubble {
return $env{'form.scantron_maxbubble'};
}
-sub scantron_partids_tograde {
- my ($resource,$cid,$uname,$udom) = @_;
- my (%analysis,@parts);
-
- if (ref($resource)) {
- my $symb = $resource->symb();
- my $result=&ssi_with_retries($resource->src(), $ssi_retries,
- ('symb' => $symb,
- 'grade_target' => 'analyze',
- 'grade_courseid' => $cid,
- 'grade_domain' => $udom,
- 'grade_username' => $uname));
- my (undef, $an) = split(/_HASH_REF__/,$result, 2);
- %analysis = &Apache::lonnet::str2hash($an);
-
- if (ref($analysis{'parts'}) eq 'ARRAY') {
- foreach my $part (@{$analysis{'parts'}}) {
- my ($id,$respid) = split(/\./,$part);
- if (!&Apache::loncommon::check_if_partid_hidden($id,$symb,$udom,$uname)) {
- push(@parts,$part);
- }
- }
- }
+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 (\%analysis,\@parts);
+ return $bubbles_per_row;
}
sub scantron_validate_missingbubbles {
@@ -7345,7 +7528,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,\%scantron_config);
+ 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);
@@ -7391,31 +7578,56 @@ sub scantron_validate_missingbubbles {
sub scantron_process_students {
- my ($r) = @_;
+ my ($r,$symb) = @_;
my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($env{'form.selectpage'});
- my ($symb)=&get_symb($r);
if (!$symb) {
return '';
}
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);
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 ($uname,$udom,%partids_by_symb);
+ my (%grader_partids_by_symb,%grader_randomlists_by_symb);
+ &graders_resources_pass(\@resources,\%grader_partids_by_symb,
+ \%grader_randomlists_by_symb,$bubbles_per_row);
+ my $resource_error;
foreach my $resource (@resources) {
- my $ressymb = $resource->symb();
- my ($analysis,$parts) =
- &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);
- $partids_by_symb{$ressymb} = $parts;
+ 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,$bubbles_per_row);
+ $grader_partids_by_symb{$ressymb} = $parts;
+ if (ref($analysis) eq 'HASH') {
+ if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {
+ $grader_randomlists_by_symb{$ressymb} =
+ $analysis->{'parts_withrandomlist'};
+ }
+ }
}
-# $r->print("geto ".scalar(@resources)." ");
+ if ($resource_error) {
+ $r->print(&navmap_errormsg());
+ return '';
+ }
+
+ my ($uname,$udom);
my $result= <
@@ -7428,8 +7640,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');
@@ -7438,8 +7650,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,\%scantron_config); # 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.
@@ -7447,7 +7663,6 @@ SCANTRONFORM
if ($ssi_error) {
$r->print("");
&ssi_print_error($r);
- $r->print(&show_grading_menu_form($symb));
&Apache::lonnet::remove_lock($lock);
return ''; # Dunno why the other returns return '' rather than just returning.
}
@@ -7480,6 +7695,31 @@ SCANTRONFORM
}
($uname,$udom)=split(/:/,$uname);
+ my (%partids_by_symb,$res_error);
+ foreach my $resource (@resources) {
+ 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) =
+ &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};
+ }
+ }
+
+ 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);
@@ -7496,11 +7736,11 @@ SCANTRONFORM
}
if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
- @resources) eq 'ssi_error') {
+ \@resources,\%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);
- $r->print(&show_grading_menu_form($symb));
&Apache::lonnet::remove_lock($lock);
return ''; # Why return ''? Beats me.
}
@@ -7514,19 +7754,32 @@ SCANTRONFORM
my $studentrecord = '';
my $counter = -1;
foreach my $resource (@resources) {
+ my $ressymb = $resource->symb();
($counter,my $recording) =
&verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
- $counter,$studentdata,\%partids_by_symb,
+ $counter,$studentdata,$partids_by_symb{$ressymb},
\%scantron_config,\%lettdig,$numletts);
$studentrecord .= $recording;
}
if ($studentrecord ne $studentdata) {
+ &Apache::lonxml::clear_problem_counter();
+ if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
+ \@resources,\%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);
+ &Apache::lonnet::remove_lock($lock);
+ delete($completedstudents{$uname});
+ return '';
+ }
$counter = -1;
$studentrecord = '';
foreach my $resource (@resources) {
+ my $ressymb = $resource->symb();
($counter,my $recording) =
&verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
- $counter,$studentdata,\%partids_by_symb,
+ $counter,$studentdata,$partids_by_symb{$ressymb},
\%scantron_config,\%lettdig,$numletts);
$studentrecord .= $recording;
}
@@ -7563,7 +7816,7 @@ SCANTRONFORM
if (&Apache::loncommon::connection_aborted($r)) { last; }
} continue {
&Apache::lonxml::clear_problem_counter();
- &Apache::lonnet::delenv('scantron\.');
+ &Apache::lonnet::delenv('scantron.');
}
&Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
&Apache::lonnet::remove_lock($lock);
@@ -7571,62 +7824,129 @@ SCANTRONFORM
# $r->print("
took $lasttime
");
$r->print("");
- $r->print(&show_grading_menu_form($symb));
return '';
}
+sub graders_resources_pass {
+ 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,$bubbles_per_row);
+ $grader_partids_by_symb->{$ressymb} = $parts;
+ if (ref($analysis) eq 'HASH') {
+ if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {
+ $grader_randomlists_by_symb->{$ressymb} =
+ $analysis->{'parts_withrandomlist'};
+ }
+ }
+ }
+ }
+ return;
+}
+
sub grade_student_bubbles {
- my ($r,$uname,$udom,$scan_record,$scancode,@resources) = @_;
- foreach my $resource (@resources) {
- my %form = ('submitted' => 'scantron',
- 'grade_target' => 'grade',
- 'grade_username'=> $uname,
- 'grade_domain' => $udom,
- 'grade_courseid'=> $env{'request.course.id'},
- 'grade_symb' => $resource->symb(),
- 'code' => $scancode);
- my $result=&ssi_with_retries($resource->src(),$ssi_retries,%form);
- return 'ssi_error' if ($ssi_error);
- last if (&Apache::loncommon::connection_aborted($r));
+ my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts,$bubbles_per_row) = @_;
+# Walk folder as student here to get resources in order student sees.
+ if (ref($resources) eq 'ARRAY') {
+ my $count = 0;
+ foreach my $resource (@{$resources}) {
+ my $ressymb = $resource->symb();
+ my %form = ('submitted' => 'scantron',
+ 'grade_target' => 'grade',
+ 'grade_username' => $uname,
+ 'grade_domain' => $udom,
+ 'grade_courseid' => $env{'request.course.id'},
+ 'grade_symb' => $ressymb,
+ 'CODE' => $scancode
+ );
+ if ($bubbles_per_row ne '') {
+ $form{'bubbles_per_row'} = $bubbles_per_row;
+ }
+ if (ref($parts) eq 'HASH') {
+ if (ref($parts->{$ressymb}) eq 'ARRAY') {
+ foreach my $part (@{$parts->{$ressymb}}) {
+ $form{'scantron_questnum_start.'.$part} =
+ 1+$env{'form.scantron.first_bubble_line.'.$count};
+ $count++;
+ }
+ }
+ }
+ my $result=&ssi_with_retries($resource->src(),$ssi_retries,%form);
+ return 'ssi_error' if ($ssi_error);
+ last if (&Apache::loncommon::connection_aborted($r));
+ }
}
return;
}
sub scantron_upload_scantron_data {
- my ($r)=@_;
- $r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'}));
+ my ($r,$symb)=@_;
+ 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');
- my $default_form_data=&defaultFormData(&get_symb($r,1));
- $r->print('
-
+
+ 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;
+ }
+
+'));
+ $r->print('
+
');
return '';
@@ -7634,8 +7954,7 @@ sub scantron_upload_scantron_data {
sub scantron_upload_scantron_data_save {
- my($r)=@_;
- my ($symb)=&get_symb($r,1);
+ my($r,$symb)=@_;
my $doanotherupload=
'
'."\n".
''."\n".
@@ -7644,55 +7963,128 @@ 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.")." ");
- if ($symb) {
- $r->print(&show_grading_menu_form($symb));
- } else {
+ $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")." ");
+ unless ($symb) {
$r->print($doanotherupload);
}
return '';
}
my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});
- $r->print(&mt("Doing upload to [_1]",$coursedata{'description'})." ");
- 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('
'.&mt("Uploading file to [_1]",$coursedata{'description'}).'
');
if (length($env{'form.upfile'}) < 2) {
- $r->print(&mt("Error: The file you attempted to upload, [_1] contained no information. Please check that you entered the correct filename.",''.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').""));
+ $r->print(&mt('[_1]Error:[_2] The file you attempted to upload, [_3] contained no information. Please check that you entered the correct filename.','','',''.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').''));
} else {
- my $result=&Apache::lonnet::finishuserfileupload($env{'form.courseid'},$env{'form.domainid'},'upfile',$fname);
- if ($result =~ m|^/uploaded/|) {
- $r->print(&mt("Success: Successfully uploaded [_1] bytes of data into location [_2]",
- (length($env{'form.upfile'})-1),
- ''.$result.""));
+ 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]',
+ '','',(length($env{'form.upfile'})-1),
+ ''.$result.''));
+ ($uploadedfile) = ($result =~ m{/([^/]+)$});
+ $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},
+ $env{'form.courseid'},$uploadedfile));
} else {
- $r->print(&mt("Error: An error ([_1]) occurred when attempting to upload the file, [_2]",
- $result,
- ''.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').""));
-
+ $r->print(&mt('[_1]Error:[_2] An error ([_3]) occurred when attempting to upload the file, [_4]',
+ '','',$result,
+ ''.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').''));
}
}
if ($symb) {
- $r->print(&scantron_selectphase($r,$uploadedfile));
+ $r->print(&scantron_selectphase($r,$uploadedfile,$symb));
} else {
$r->print($doanotherupload);
}
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 .= '"'.$desc.'", ';
+ } elsif ($i==$numwithformat-2) {
+ $format_descs .= '"'.$desc.'" '.&mt('and').' ';
+ } elsif ($i==$numwithformat-1) {
+ $format_descs .= '"'.$desc.'"';
+ }
+ }
+ my $showpct = sprintf("%.0f",$max_match_pct).'%';
+ $output .= ' '.&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]).',''.$showpct.'',''.$max_match_count.'',$format_descs).
+ ' '.&mt('A low percentage of matches results from one of the following:').'
'.
+ '
'.&mt('The file was uploaded to the wrong course').'
'.
+ '
'.&mt('The data are not in the format expected for the domain: [_1]',
+ ''.$cdom.'').'
'.
+ '
'.&mt('Students did not bubble their IDs, or mis-bubbled them').'
'.
+ '
'.&mt('The course roster is not up to date').'
'.
+ '
';
+ }
+ } else {
+ $output = ''.&mt('Uploaded file contained no data').'';
+ }
+ return $output;
+}
+
sub valid_file {
my ($requested_file)=@_;
foreach my $filename (sort(&scantron_filenames())) {
@@ -7702,8 +8094,8 @@ sub valid_file {
}
sub scantron_download_scantron_data {
- my ($r)=@_;
- my $default_form_data=&defaultFormData(&get_symb($r,1));
+ my ($r,$symb)=@_;
+ 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'};
@@ -7713,7 +8105,6 @@ sub scantron_download_scantron_data {
'.&mt('The requested file name was invalid.').'
');
- $r->print(&show_grading_menu_form(&get_symb($r,1)));
return;
}
my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;
@@ -7736,15 +8127,12 @@ sub scantron_download_scantron_data {
'','').'
');
- $r->print(&show_grading_menu_form(&get_symb($r,1)));
return '';
}
sub checkscantron_results {
- my ($r) = @_;
- my ($symb)=&get_symb($r);
+ my ($r,$symb) = @_;
if (!$symb) {return '';}
- my $grading_menu_button=&show_grading_menu_form($symb);
my $cid = $env{'request.course.id'};
my %lettdig = &letter_to_digits();
my $numletts = scalar(keys(%lettdig));
@@ -7754,19 +8142,21 @@ 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);
my $navmap=Apache::lonnavmaps::navmap->new();
- my $map=$navmap->getResourceByUrl($sequence);
- my @resources=$navmap->retrieveResources($map,undef,1,0);
- my ($uname,$udom,%partids_by_symb);
- foreach my $resource (@resources) {
- my $ressymb = $resource->symb();
- my ($analysis,$parts) =
- &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);
- $partids_by_symb{$ressymb} = $parts;
+ 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 ($uname,$udom);
my (%scandata,%lastname,%bylast);
$r->print('
'."\n");
@@ -7775,12 +8165,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,$uname,$started);
-
- &Apache::grades::scantron_get_maxbubble(); # Need the bubble lines array to parse.
+ my ($username,$domain,$started);
+ my $nav_error;
+ &scantron_get_maxbubble(\$nav_error,\%scantron_config); # 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');
@@ -7821,9 +8215,18 @@ sub checkscantron_results {
($username,$domain)=split(/:/,$uname);
my $counter = -1;
foreach my $resource (@resources) {
+ 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,undef,$bubbles_per_row);
+ } else {
+ $parts = $grader_partids_by_symb{$ressymb};
+ }
($counter,my $recording) =
&verify_scantron_grading($resource,$domain,$username,$cid,$counter,
- $scandata{$pid},\%partids_by_symb,
+ $scandata{$pid},$parts,
\%scantron_config,\%lettdig,$numletts);
$record{$pid} .= $recording;
}
@@ -7844,14 +8247,14 @@ sub checkscantron_results {
if ($scandata{$pid} eq $record{$pid}) {
my $css_class = ($passed % 2)?'LC_odd_row':'LC_even_row';
$okstudents .= '
'.&mt('Comparison of scantron data (including corrections) with corresponding submission records (most recent submission) for [quant,_1,student] ([_2] scantron lines/student).',$numstudents,$env{'form.scantron_maxbubble'}).'
');
+ $r->print(
+ '
'
+ .&mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for [_1][quant,_2,student][_3] ([quant,_4,bubblesheet line] per student).',
+ '',
+ $numstudents,
+ '',
+ $env{'form.scantron_maxbubble'})
+ .'
'
+ );
$r->print('
'.&mt('Exact matches for [quant,_1,student].',$passed).' '.&mt('Discrepancies detected for [quant,_1,student].',$failed).'
');
if ($passed) {
- $r->print(&mt('Students with exact correspondence between scantron data and submissions are as follows:').'
');
+ $r->print(&mt('Students with exact correspondence between bubblesheet data and submissions are as follows:').'
'.
@@ -7874,29 +8285,28 @@ sub checkscantron_results {
&Apache::loncommon::end_data_table().' ');
}
if ($failed) {
- $r->print(&mt('Students with differences between scantron data and submissions are as follows:').'
');
+ $r->print(&mt('Students with differences between bubblesheet data and submissions are as follows:').'
'.
&Apache::loncommon::end_data_table_header_row()."\n".
$badstudents."\n".
&Apache::loncommon::end_data_table()).' '.
- &mt('Differences can occur if submissions were modified using manual grading after a scantron grading pass.').' '.&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.').' '.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original bubblesheets.');
}
- $r->print(' '.$grading_menu_button);
+ $r->print(' ');
return;
}
sub verify_scantron_grading {
- my ($resource,$domain,$username,$cid,$counter,$scandata,$partids_by_symb,
+ my ($resource,$domain,$username,$cid,$counter,$scandata,$partids,
$scantron_config,$lettdig,$numletts) = @_;
my ($record,%expected,%startpos);
return ($counter,$record) if (!ref($resource));
return ($counter,$record) if (!$resource->is_problem());
my $symb = $resource->symb();
- return ($counter,$record) if (ref($partids_by_symb) ne 'HASH');
- return ($counter,$record) if (ref($partids_by_symb->{$symb}) ne 'ARRAY');
- foreach my $part_id (@{$partids_by_symb->{$symb}}) {
+ return ($counter,$record) if (ref($partids) ne 'ARRAY');
+ foreach my $part_id (@{$partids}) {
$counter ++;
$expected{$part_id} = 0;
if ($env{"form.scantron.sub_bubblelines.$counter"}) {
@@ -7989,7 +8399,7 @@ sub verify_scantron_grading {
}
}
}
- foreach my $part_id (@{$partids_by_symb->{$symb}}) {
+ foreach my $part_id (@{$partids}) {
if ($recorded{$part_id} eq '') {
for (my $i=0; $i<$expected{$part_id}; $i++) {
for (my $j=0; $j<$scantron_config->{'Qlength'}; $j++) {
@@ -8026,48 +8436,34 @@ sub letter_to_digits {
#-------------------------- Menu interface -------------------------
#
-#--- Show a Grading Menu button - Calls the next routine ---
-sub show_grading_menu_form {
- my ($symb)=@_;
- my $result.='
\n";
# Attempt to restore parameters from last session, set defaults if not present
my %Saveable_Parameters=&clicker_grading_parameters();
&Apache::loncommon::restore_course_settings('grades_clicker',
@@ -8443,11 +8785,11 @@ 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"';
}
}
- my $upload=&mt("Upload File");
+ my $upload=&mt("Evaluate File");
my $type=&mt("Type");
my $attendance=&mt("Award points just for participation");
my $personnel=&mt("Correctness determined from response by course personnel");
@@ -8457,11 +8799,10 @@ sub process_clicker {
my $pcorrect=&mt("Percentage points for correct solution");
my $pincorrect=&mt("Percentage points for incorrect solution");
my $selectform=&Apache::loncommon::select_form($env{'form.upfiletype'},'upfiletype',
- ('iclicker' => 'i>clicker',
- 'interwrite' => 'interwrite PRS'));
+ {'iclicker' => 'i>clicker',
+ 'interwrite' => 'interwrite PRS'});
$symb = &Apache::lonenc::check_encrypt($symb);
- $result.=<
+ $result.= &Apache::lonhtmlcommon::scripttag(<
+ENDUPFORM
+ $result.= <
-
-
-
-
-
+ENDUPFORM
+ $result.='
'.
+ &Apache::loncommon::end_data_table_row().
+ &Apache::loncommon::end_data_table();
return $result;
}
sub process_clicker_file {
- my ($r)=@_;
- my ($symb)=&get_symb($r);
+ my ($r,$symb)=@_;
if (!$symb) {return '';}
my %Saveable_Parameters=&clicker_grading_parameters();
&Apache::loncommon::store_course_settings('grades_clicker',
\%Saveable_Parameters);
-
- my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});
+ my $result='';
if (($env{'form.gradingmechanism'} eq 'specific') && ($env{'form.specificid'}!~/\w/)) {
$result.=''.&mt('You need to specify a clicker ID for the correct answer').'';
- return $result.&show_grading_menu_form($symb);
+ return $result;
}
if (($env{'form.gradingmechanism'} eq 'given') && ($env{'form.givenanswer'}!~/\S/)) {
$result.=''.&mt('You need to specify the correct answer').'';
- return $result.&show_grading_menu_form($symb);
+ return $result;
}
my $foundgiven=0;
if ($env{'form.gradingmechanism'} eq 'given') {
$env{'form.givenanswer'}=~s/^\s*//gs;
$env{'form.givenanswer'}=~s/\s*$//gs;
- $env{'form.givenanswer'}=~s/[^a-zA-Z0-9\.\*\-]+/\,/g;
+ $env{'form.givenanswer'}=~s/[^a-zA-Z0-9\.\*\-\+]+/\,/g;
$env{'form.givenanswer'}=uc($env{'form.givenanswer'});
my @answers=split(/\,/,$env{'form.givenanswer'});
$foundgiven=$#answers+1;
@@ -8589,7 +8933,7 @@ sub process_clicker_file {
$result.="\n";
if ($number==0) {
$result.=''.&mt('No IDs found to determine correct answer').'';
- return $result.&show_grading_menu_form($symb);
+ return $result;
}
}
if (length($env{'form.upfile'}) < 2) {
@@ -8597,23 +8941,22 @@ sub process_clicker_file {
'',
'',
''.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'');
- return $result.&show_grading_menu_form($symb);
+ return $result;
}
# Were able to get all the info needed, now analyze the file
$result.=&Apache::loncommon::studentbrowser_javascript();
$symb = &Apache::lonenc::check_encrypt($symb);
- my $heading=&mt('Scanning clicker file');
- $result.=(<
-
-
@@ -8638,7 +8981,7 @@ ENDHEADER
' ';
if (($env{'form.gradingmechanism'} eq 'given') && ($number!=$foundgiven)) {
$result.=''.&mt('Number of given answers does not agree with number of questions in file.').'';
- return $result.&show_grading_menu_form($symb);
+ return $result;
}
# Remember Question Titles
# FIXME: Possibly need delimiter other than ":"
@@ -8658,7 +9001,9 @@ ENDHEADER
} elsif ($clicker_ids{$id}) {
if ($clicker_ids{$id}=~/\,/) {
# More than one user with the same clicker!
- $result.="\n".&mt('Clicker registered more than once').": ".$id." ";
+ $result.="".&Apache::loncommon::end_data_table_row().
+ &Apache::loncommon::start_data_table_row()."
".
+ &mt('Clicker registered more than once').": ".$id." ";
$result.="\n".''.
"
'.
+ &Apache::loncommon::end_data_table_row().
+ &Apache::loncommon::end_data_table();
+ return $result;
}
sub iclicker_eval {
@@ -8722,6 +9070,7 @@ sub iclicker_eval {
$id=~s/^[\#0]+//;
for (my $i=0;$i<$number;$i++) {
my $idx=3+$i*6;
+ $entries[$idx]=~s/[^a-zA-Z0-9\.\*\-\+]+//g;
push(@idresponses,$entries[$idx]);
}
$$responses{$id}=join(',',@idresponses);
@@ -8762,22 +9111,22 @@ sub interwrite_eval {
}
sub assign_clicker_grades {
- my ($r)=@_;
- my ($symb)=&get_symb($r);
+ my ($r,$symb)=@_;
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
- my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});
-
- my $heading=&mt('Assigning grades based on clicker file');
- $result.=(<
-
-$heading
-ENDHEADER
+ my $result=&Apache::loncommon::start_data_table().
+ &Apache::loncommon::start_data_table_header_row().
+ '
'.
+ &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.','','').
+ '
');
}
}
if ($ssi_error) {
&ssi_print_error($request);
}
+ &Apache::lonquickgrades::endGradeScreen($request);
$request->print(&Apache::loncommon::end_page());
&reset_caches();
- return '';
+ return OK;
}
1;
@@ -9095,6 +9553,15 @@ 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().
+
+ $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()
@@ -9158,7 +9625,12 @@ ssi_with_retries()
=item scantron_validate_ID() :
Validates all scanlines in the selected file to not have any
- invalid or underspecified student IDs
+ 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