--- loncom/homework/grades.pm 2009/05/01 01:07:49 1.565 +++ loncom/homework/grades.pm 2009/05/07 19:36:45 1.572 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.565 2009/05/01 01:07:49 raeburn Exp $ +# $Id: grades.pm,v 1.572 2009/05/07 19:36:45 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -4090,14 +4090,14 @@ sub csvuploadassign { $countdone++; } } - $request->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)); @@ -5069,10 +5069,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 +5090,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 +5109,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 +5136,7 @@ sub scantron_selectphase { '.&mt('File to upload: [_1]','').'
- + '); @@ -5178,7 +5178,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". @@ -6251,7 +6251,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 +6777,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(" @@ -6791,10 +6791,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(" "); @@ -7018,7 +7018,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. ")."
"); } @@ -7740,6 +7740,8 @@ sub scantron_upload_scantron_data { +

'.&mt('Send scanned bubblesheet data to a course').'

+
'.$default_form_data. &Apache::lonhtmlcommon::start_pick_box(). @@ -7785,36 +7787,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) { @@ -7825,6 +7816,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:').''; + } + } else { + $output = ''.&mt('Uploaded file contained no data').''; + } + return $output; +} + sub valid_file { my ($requested_file)=@_; foreach my $filename (sort(&scantron_filenames())) { @@ -8000,10 +8077,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').''. @@ -8012,14 +8089,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; @@ -8580,7 +8657,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"'; } } @@ -8644,11 +8721,11 @@ function sanitycheck() {
-
-
-
+
+
+
-
+