print(''.&mt("Saved [_1] students",$countdone)." \n");
+ $request->print(' '.&Apache::lonhtmlcommon::confirm_success(&mt("Saved scores for [quant,_1,student]",$countdone),$countdone==0));
if (@skipped) {
- $request->print(''.&mt('Skipped Students').'
');
- foreach my $student (@skipped) { $request->print("$student \n"); }
+ $request->print(' '.&Apache::lonhtmlcommon::confirm_success(&mt('No scores stored for the following username(s):'),1).' ');
+ $request->print(join(', ',@skipped));
}
if (@notallowed) {
- $request->print(''.&mt('Students Not Allowed to Modify').'
');
- foreach my $student (@notallowed) { $request->print("$student \n"); }
+ $request->print(' '.&Apache::lonhtmlcommon::confirm_success(&mt('Modification of scores not allowed for the following username(s):'),1).' ');
+ $request->print(join(', ',@notallowed));
}
$request->print(" \n");
$request->print(&show_grading_menu_form($symb));
@@ -4467,13 +4471,16 @@ sub displaySubByDates {
my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
: ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
- $displaySub[0].=''.&mt('Part:').' '.$display_part.' ';
- $displaySub[0].='('.&mt('ID').' '.
- $responseId.') ';
+ $displaySub[0].=''.&mt('Part: [_1]',$display_part).' '
+ .' '
+ .'('.&mt('Part ID: [_1]',$responseId).')'
+ .' '
+ .' ';
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'
@@ -4484,7 +4491,8 @@ sub displaySubByDates {
&get_order($partid,$responseId,$symb,$uname,$udom,
$no_increment);
}
- $displaySub[0].=' '.
+ $displaySub[0].=''; # /nobreak
+ $displaySub[0].=' '.
&cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom).' ';
}
}
@@ -5069,10 +5077,10 @@ sub scantron_selectphase {
'.&mt('Sequence to grade:').' '.$sequence_selector.'
'.&Apache::loncommon::end_data_table_row().'
'.&Apache::loncommon::start_data_table_row().'
- '.&mt('Filename of scoring office file:').' '.$file_selector.'
+ '.&mt('Filename of bubblesheet data file:').' '.$file_selector.'
'.&Apache::loncommon::end_data_table_row().'
'.&Apache::loncommon::start_data_table_row().'
- '.&mt('Format of data file:').' '.$format_selector.'
+ '.&mt('Format of bubblesheet data file:').' '.$format_selector.'
'.&Apache::loncommon::end_data_table_row().'
'.&Apache::loncommon::start_data_table_row().'
'.&mt('Saved CODEs to validate against:').' '.$CODE_selector.'
@@ -5090,7 +5098,7 @@ sub scantron_selectphase {
'.&Apache::loncommon::end_data_table_row().'
'.&Apache::loncommon::start_data_table_row().'
-
+
'.&Apache::loncommon::end_data_table_row().'
'.&Apache::loncommon::end_data_table().'
@@ -5109,7 +5117,7 @@ sub scantron_selectphase {
'.&Apache::loncommon::start_data_table('LC_scantron_action').'
'.&Apache::loncommon::start_data_table_header_row().'
- '.&mt('Specify a Scantron data file to upload.').'
+ '.&mt('Specify a bubblesheet data file to upload.').'
'.&Apache::loncommon::end_data_table_header_row().'
'.&Apache::loncommon::start_data_table_row().'
@@ -5136,7 +5144,7 @@ sub scantron_selectphase {
'.&mt('File to upload: [_1]',' ').'
-
+
');
@@ -5178,7 +5186,7 @@ sub scantron_selectphase {
&Apache::loncommon::start_data_table('LC_scantron_action')."\n".
&Apache::loncommon::start_data_table_header_row()."\n".
'
- '.&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".
' '."\n".
&Apache::loncommon::end_data_table_header_row()."\n".
&Apache::loncommon::start_data_table_row()."\n".
@@ -5200,7 +5208,7 @@ sub scantron_selectphase {
&Apache::loncommon::start_data_table_row()."\n".
''."\n".
' '."\n".
- ' '."\n".
+ ' '."\n".
' '."\n".
&Apache::loncommon::end_data_table_row()."\n".
&Apache::loncommon::end_data_table()."\n".
@@ -5241,7 +5249,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
@@ -5341,7 +5349,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)
@@ -6251,7 +6259,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("Alternatively, the 'Review bubblesheet data' utility (see grading menu) can be used for all students after grading is complete.").' '.
' '.
' '."\n");
} else {
@@ -6777,10 +6785,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("
-
+
".&mt("Use the similar CODE [_1] instead.",
"".$testcode." ")."
@@ -6791,10 +6799,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("
-
+
".&mt("Use the CODE [_1] that is was on the paper, ignoring the error.",
"".$$scan_record{'scantron.CODE'}." ")."
");
@@ -7018,7 +7026,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. ")." ");
}
@@ -7475,8 +7483,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');
@@ -7701,12 +7709,14 @@ 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 = ''.&mt('Syllabus').' '.
+ (' 'x2).&mt('(shows course personnel)');
my $default_form_data=&defaultFormData(&get_symb($r,1));
$r->print('
+'.&mt('Send scanned bubblesheet data to a course').'
+
');
return '';
@@ -7751,7 +7786,7 @@ sub scantron_upload_scantron_data_save {
if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&
!&Apache::lonnet::allowed('usc',
$env{'form.domainid'}.'_'.$env{'form.courseid'})) {
- $r->print(&mt("You are not allowed to upload Scantron data to the requested course.")." ");
+ $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")." ");
if ($symb) {
$r->print(&show_grading_menu_form($symb));
} else {
@@ -7760,36 +7795,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'})." ");
- 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) {
@@ -7800,6 +7824,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 .= '"'.$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())) {
@@ -7975,10 +8085,10 @@ sub checkscantron_results {
}
}
}
- $r->print(''.&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 [quant,_1,student] ([_2] scantron lines/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:').' ');
$r->print(&Apache::loncommon::start_data_table()."\n".
&Apache::loncommon::start_data_table_header_row()."\n".
''.&mt('Source').' '.&mt('Bubble records').' '.&mt('Name').' '.&mt('ID').' '.
@@ -7987,14 +8097,14 @@ 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:').' ');
$r->print(&Apache::loncommon::start_data_table()."\n".
&Apache::loncommon::start_data_table_header_row()."\n".
''.&mt('Source').' '.&mt('Bubble records').' '.&mt('Name').' '.&mt('ID').' '.
&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);
return;
@@ -8555,7 +8665,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"';
}
}
@@ -8619,11 +8729,11 @@ function sanitycheck() {
$type: $selectform
- $attendance
- $personnel
- $specific
+ $attendance
+ $personnel
+ $specific
- $given
+ $given
@@ -8942,13 +9052,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;