version 1.528.2.8, 2009/01/06 21:58:06
|
version 1.533, 2008/12/08 18:25:13
|
Line 26
|
Line 26
|
# http://www.lon-capa.org/ |
# http://www.lon-capa.org/ |
# |
# |
|
|
|
|
|
|
package Apache::grades; |
package Apache::grades; |
use strict; |
use strict; |
use Apache::style; |
use Apache::style; |
Line 58 my $ssi_error_resource;
|
Line 60 my $ssi_error_resource;
|
my $ssi_error_message; |
my $ssi_error_message; |
|
|
|
|
# Do an ssi with retries: |
|
# While I'd love to factor out this with the vesrion in lonprintout, |
|
# that would either require a data coupling between modules, which I refuse to perpetuate |
|
# (there's quite enough of that already), or would require the invention of another infrastructure |
|
# I'm not quite ready to invent (e.g. an ssi_with_retry object). |
|
# |
|
# At least the logic that drives this has been pulled out into loncommon. |
|
|
|
|
|
# |
|
# ssi_with_retries - Does the server side include of a resource. |
|
# if the ssi call returns an error we'll retry it up to |
|
# the number of times requested by the caller. |
|
# If we still have a proble, no text is appended to the |
|
# output and we set some global variables. |
|
# to indicate to the caller an SSI error occurred. |
|
# All of this is supposed to deal with the issues described |
|
# in LonCAPA BZ 5631 see: |
|
# http://bugs.lon-capa.org/show_bug.cgi?id=5631 |
|
# by informing the user that this happened. |
|
# |
|
# Parameters: |
|
# resource - The resource to include. This is passed directly, without |
|
# interpretation to lonnet::ssi. |
|
# form - The form hash parameters that guide the interpretation of the resource |
|
# |
|
# retries - Number of retries allowed before giving up completely. |
|
# Returns: |
|
# On success, returns the rendered resource identified by the resource parameter. |
|
# Side Effects: |
|
# The following global variables can be set: |
|
# ssi_error - If an unrecoverable error occurred this becomes true. |
|
# It is up to the caller to initialize this to false |
|
# if desired. |
|
# ssi_error_resource - If an unrecoverable error occurred, this is the value |
|
# of the resource that could not be rendered by the ssi |
|
# call. |
|
# ssi_error_message - The error string fetched from the ssi response |
|
# in the event of an error. |
|
# |
|
sub ssi_with_retries { |
sub ssi_with_retries { |
my ($resource, $retries, %form) = @_; |
my ($resource, $retries, %form) = @_; |
my ($content, $response) = &Apache::loncommon::ssi_with_retries($resource, $retries, %form); |
my ($content, $response) = &Apache::loncommon::ssi_with_retries($resource, $retries, %form); |
Line 2135 KEYWORDS
|
Line 2097 KEYWORDS
|
' )</span> '; |
' )</span> '; |
my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record); |
my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record); |
if (@$files) { |
if (@$files) { |
$lastsubonly.='<br /><span class="LC_warning">'.&mt('Like all files provided by users, this file may contain viruses').'</span><br />'; |
$lastsubonly.='<br /><span class="LC_warning">'.&mt('Like all files provided by users, this file may contain virusses').'</span><br />'; |
my $file_counter = 0; |
my $file_counter = 0; |
foreach my $file (@$files) { |
foreach my $file (@$files) { |
$file_counter++; |
$file_counter++; |
Line 2358 sub get_last_submission {
|
Line 2320 sub get_last_submission {
|
$$returnhash{$version.':keys'}))) { |
$$returnhash{$version.':keys'}))) { |
$lasthash{$key}=$$returnhash{$version.':'.$key}; |
$lasthash{$key}=$$returnhash{$version.':'.$key}; |
$timestamp = |
$timestamp = |
&Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'}); |
scalar(localtime($$returnhash{$version.':timestamp'})); |
} |
} |
} |
} |
foreach my $key (keys(%lasthash)) { |
foreach my $key (keys(%lasthash)) { |
Line 2796 sub handback_files {
|
Line 2758 sub handback_files {
|
$newflg.'_'.$part_resp.'_returndoc'.$file_counter, |
$newflg.'_'.$part_resp.'_returndoc'.$file_counter, |
$save_file_name); |
$save_file_name); |
if ($result !~ m|^/uploaded/|) { |
if ($result !~ m|^/uploaded/|) { |
$request->print('<br /><span class="LC_error">'. |
$request->print('<span class="LC_error">An error occurred ('.$result. |
&mt('An error occurred ([_1]) while trying to upload [_2].', |
') while trying to upload '.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'</span><br />'); |
$result,$newflg.'_'.$part_resp.'_returndoc'.$file_counter). |
|
'</span>'); |
|
} else { |
} else { |
# mark the file as read only |
# mark the file as read only |
my @files = ($save_file_name); |
my @files = ($save_file_name); |
Line 6613 sub scantron_validate_sequence {
|
Line 6573 sub scantron_validate_sequence {
|
return (0,$currentphase+1); |
return (0,$currentphase+1); |
} |
} |
|
|
=pod |
|
|
|
=item scantron_validate_ID |
|
|
|
Validates all scanlines in the selected file to not have any |
|
invalid or underspecified student IDs |
|
|
|
=cut |
|
|
|
sub scantron_validate_ID { |
sub scantron_validate_ID { |
my ($r,$currentphase) = @_; |
my ($r,$currentphase) = @_; |
Line 6686 sub scantron_validate_ID {
|
Line 6639 sub scantron_validate_ID {
|
return (0,$currentphase+1); |
return (0,$currentphase+1); |
} |
} |
|
|
=pod |
|
|
|
=item scantron_get_correction |
|
|
|
Builds the interface screen to interact with the operator to fix a |
|
specific error condition in a specific scanline |
|
|
|
Arguments: |
|
$r - Apache request object |
|
$i - number of the current scanline |
|
$scan_record - hash ref as returned from &scantron_parse_scanline() |
|
$scan_config - hash ref as returned from &get_scantron_config() |
|
$line - full contents of the current scanline |
|
$error - error condition, valid values are |
|
'incorrectCODE', 'duplicateCODE', |
|
'doublebubble', 'missingbubble', |
|
'duplicateID', 'incorrectID' |
|
$arg - extra information needed |
|
For errors: |
|
- duplicateID - paper number that this studentID was seen before on |
|
- duplicateCODE - array ref of the paper numbers this CODE was |
|
seen on before |
|
- incorrectCODE - current incorrect CODE |
|
- doublebubble - array ref of the bubble lines that have double |
|
bubble errors |
|
- missingbubble - array ref of the bubble lines that have missing |
|
bubble errors |
|
|
|
=cut |
|
|
|
sub scantron_get_correction { |
sub scantron_get_correction { |
my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_; |
my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_; |
Line 7285 sub scantron_validate_doublebubble {
|
Line 7209 sub scantron_validate_doublebubble {
|
return (0,$currentphase+1); |
return (0,$currentphase+1); |
} |
} |
|
|
=pod |
|
|
|
=item scantron_get_maxbubble |
|
|
|
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() |
|
for what the current value of the problem counter is. |
|
|
|
Caches the results to $env{'form.scantron_maxbubble'}, |
|
$env{'form.scantron.bubble_lines.n'}, |
|
$env{'form.scantron.first_bubble_line.n'} and |
|
$env{"form.scantron.sub_bubblelines.n"} |
|
which are the total number of bubble, lines, the number of bubble |
|
lines for response n and number of the first bubble line for response n, |
|
and a comma separated list of numbers of bubble lines for sub-questions |
|
(for optionresponse, matchresponse, and rankresponse items), for response n. |
|
|
|
=cut |
|
|
|
sub scantron_get_maxbubble { |
sub scantron_get_maxbubble { |
if (defined($env{'form.scantron_maxbubble'}) && |
if (defined($env{'form.scantron_maxbubble'}) && |
Line 7333 sub scantron_get_maxbubble {
|
Line 7238 sub scantron_get_maxbubble {
|
my $response_number = 0; |
my $response_number = 0; |
my $bubble_line = 0; |
my $bubble_line = 0; |
foreach my $resource (@resources) { |
foreach my $resource (@resources) { |
my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,$udom); |
my $symb = $resource->symb(); |
if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) { |
|
foreach my $part_id (@{$parts}) { |
|
|
|
my $lines; |
|
|
|
# TODO - make this a persistent hash not an array. |
|
|
|
# optionresponse, matchresponse and rankresponse type items |
|
# render as separate sub-questions in exam mode. |
|
if (($analysis->{$part_id.'.type'} eq 'optionresponse') || |
|
($analysis->{$part_id.'.type'} eq 'matchresponse') || |
|
($analysis->{$part_id.'.type'} eq 'rankresponse')) { |
|
my ($numbub,$numshown); |
|
if ($analysis->{$part_id.'.type'} eq 'optionresponse') { |
|
if (ref($analysis->{$part_id.'.options'}) eq 'ARRAY') { |
|
$numbub = scalar(@{$analysis->{$part_id.'.options'}}); |
|
} |
|
} elsif ($analysis->{$part_id.'.type'} eq 'matchresponse') { |
|
if (ref($analysis->{$part_id.'.items'}) eq 'ARRAY') { |
|
$numbub = scalar(@{$analysis->{$part_id.'.items'}}); |
|
} |
|
} elsif ($analysis->{$part_id.'.type'} eq 'rankresponse') { |
|
if (ref($analysis->{$part_id.'.foils'}) eq 'ARRAY') { |
|
$numbub = scalar(@{$analysis->{$part_id.'.foils'}}); |
|
} |
|
} |
|
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) { |
|
$inner_bubble_lines++; |
|
} |
|
for (my $i=0; $i<$numshown; $i++) { |
|
$subdivided_bubble_lines{$response_number} .= |
|
$inner_bubble_lines.','; |
|
} |
|
$subdivided_bubble_lines{$response_number} =~ s/,$//; |
|
$lines = $numshown * $inner_bubble_lines; |
|
} else { |
|
$lines = $analysis->{"$part_id.bubble_lines"}; |
|
} |
|
|
|
$first_bubble_line{$response_number} = $bubble_line; |
my (@parts,@allparts,@possible_parts); |
$bubble_lines_per_response{$response_number} = $lines; |
|
$responsetype_per_response{$response_number} = |
|
$analysis->{$part_id.'.type'}; |
|
$response_number++; |
|
|
|
$bubble_line += $lines; |
# Need to retrieve part IDs and response IDs because essayresponse, |
$total_lines += $lines; |
# reactionresponse and organicresponse items are not included in |
} |
# $analysis{'parts'} from lonnet::ssi. |
|
if (ref($resource->parts()) eq 'ARRAY') { |
|
foreach my $part (@{$resource->parts()}) { |
|
if (!&Apache::loncommon::check_if_partid_hidden($part,$symb,$udom,$uname)) { |
|
my @resp_ids = $resource->responseIds($part); |
|
foreach my $id (@resp_ids) { |
|
my $part_id = $part.'.'.$id; |
|
push(@possible_parts,$part_id); |
|
} |
|
} |
|
} |
} |
} |
} |
|
&Apache::lonnet::delenv('scantron\.'); |
|
|
|
&save_bubble_lines(); |
|
$env{'form.scantron_maxbubble'} = |
|
$total_lines; |
|
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, |
my $result=&ssi_with_retries($resource->src(), $ssi_retries, |
('symb' => $symb, |
('symb' => $symb, |
'grade_target' => 'analyze', |
'grade_target' => 'analyze', |
'grade_courseid' => $cid, |
'grade_courseid' => $cid, |
'grade_domain' => $udom, |
'grade_domain' => $udom, |
'grade_username' => $uname)); |
'grade_username' => $uname)); |
my (undef, $an) = split(/_HASH_REF__/,$result, 2); |
my (undef, $an) = |
%analysis = &Apache::lonnet::str2hash($an); |
split(/_HASH_REF__/,$result, 2); |
|
|
|
my %analysis = &Apache::lonnet::str2hash($an); |
|
|
if (ref($analysis{'parts'}) eq 'ARRAY') { |
if (ref($analysis{'parts'}) eq 'ARRAY') { |
foreach my $part (@{$analysis{'parts'}}) { |
foreach my $part (@{$analysis{'parts'}}) { |
Line 7420 sub scantron_partids_tograde {
|
Line 7276 sub scantron_partids_tograde {
|
} |
} |
} |
} |
} |
} |
} |
# Add part_ids for any essayresponse, reactionresponse or |
return (\%analysis,\@parts); |
# organicresponse items. |
} |
foreach my $part_id (@possible_parts) { |
|
if (grep(/^\Q$part_id\E$/,@parts)) { |
|
push(@allparts,$part_id); |
|
} else { |
|
if (($analysis{$part_id.'.type'} eq 'essayresponse') || |
|
($analysis{$part_id.'.type'} eq 'reactionresponse') || |
|
($analysis{$part_id.'.type'} eq 'organicresponse')) { |
|
push(@allparts,$part_id); |
|
} |
|
} |
|
} |
|
|
=pod |
foreach my $part_id (@allparts) { |
|
my $lines; |
|
|
=item scantron_validate_missingbubbles |
# TODO - make this a persistent hash not an array. |
|
|
Validates all scanlines in the selected file to not have any |
# optionresponse, matchresponse and rankresponse type items |
answers that don't have bubbles that have not been verified |
# render as separate sub-questions in exam mode. |
to be bubble free. |
if (($analysis{$part_id.'.type'} eq 'optionresponse') || |
|
($analysis{$part_id.'.type'} eq 'matchresponse') || |
|
($analysis{$part_id.'.type'} eq 'rankresponse')) { |
|
my ($numbub,$numshown); |
|
if ($analysis{$part_id.'.type'} eq 'optionresponse') { |
|
if (ref($analysis{$part_id.'.options'}) eq 'ARRAY') { |
|
$numbub = scalar(@{$analysis{$part_id.'.options'}}); |
|
} |
|
} elsif ($analysis{$part_id.'.type'} eq 'matchresponse') { |
|
if (ref($analysis{$part_id.'.items'}) eq 'ARRAY') { |
|
$numbub = scalar(@{$analysis{$part_id.'.items'}}); |
|
} |
|
} elsif ($analysis{$part_id.'.type'} eq 'rankresponse') { |
|
if (ref($analysis{$part_id.'.foils'}) eq 'ARRAY') { |
|
$numbub = scalar(@{$analysis{$part_id.'.foils'}}); |
|
} |
|
} |
|
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) { |
|
$inner_bubble_lines++; |
|
} |
|
for (my $i=0; $i<$numshown; $i++) { |
|
$subdivided_bubble_lines{$response_number} .= |
|
$inner_bubble_lines.','; |
|
} |
|
$subdivided_bubble_lines{$response_number} =~ s/,$//; |
|
$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; |
|
$responsetype_per_response{$response_number} = |
|
$analysis{$part_id.'.type'}; |
|
$response_number++; |
|
|
|
$bubble_line += $lines; |
|
$total_lines += $lines; |
|
} |
|
|
|
} |
|
&Apache::lonnet::delenv('scantron\.'); |
|
|
|
&save_bubble_lines(); |
|
$env{'form.scantron_maxbubble'} = |
|
$total_lines; |
|
return $env{'form.scantron_maxbubble'}; |
|
} |
|
|
=cut |
|
|
|
sub scantron_validate_missingbubbles { |
sub scantron_validate_missingbubbles { |
my ($r,$currentphase) = @_; |
my ($r,$currentphase) = @_; |
Line 7487 sub scantron_validate_missingbubbles {
|
Line 7405 sub scantron_validate_missingbubbles {
|
return (0,$currentphase+1); |
return (0,$currentphase+1); |
} |
} |
|
|
=pod |
|
|
|
=item scantron_process_students |
|
|
|
Routine that does the actual grading of the bubble sheet information. |
|
|
|
The parsed scanline hash is added to %env |
|
|
|
Then foreach unskipped scanline it does an &Apache::lonnet::ssi() |
|
foreach resource , with the form data of |
|
|
|
'submitted' =>'scantron' |
|
'grade_target' =>'grade', |
|
'grade_username'=> username of student |
|
'grade_domain' => domain of student |
|
'grade_courseid'=> of course |
|
'grade_symb' => symb of resource to grade |
|
|
|
This triggers a grading pass. The problem grading code takes care |
|
of converting the bubbled letter information (now in %env) into a |
|
valid submission. |
|
|
|
=cut |
|
|
|
sub scantron_process_students { |
sub scantron_process_students { |
my ($r) = @_; |
my ($r) = @_; |
Line 7528 sub scantron_process_students {
|
Line 7423 sub scantron_process_students {
|
my $navmap=Apache::lonnavmaps::navmap->new(); |
my $navmap=Apache::lonnavmaps::navmap->new(); |
my $map=$navmap->getResourceByUrl($sequence); |
my $map=$navmap->getResourceByUrl($sequence); |
my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); |
my @resources=$navmap->retrieveResources($map,\&scantron_filter,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; |
|
} |
|
# $r->print("geto ".scalar(@resources)."<br />"); |
# $r->print("geto ".scalar(@resources)."<br />"); |
my $result= <<SCANTRONFORM; |
my $result= <<SCANTRONFORM; |
<form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload"> |
<form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload"> |
Line 7545 SCANTRONFORM
|
Line 7432 SCANTRONFORM
|
$r->print($result); |
$r->print($result); |
|
|
my @delayqueue; |
my @delayqueue; |
my (%completedstudents,,%scandata); |
my %completedstudents; |
|
|
my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam')); |
my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam')); |
my $count=&get_todo_count($scanlines,$scan_data); |
my $count=&get_todo_count($scanlines,$scan_data); |
Line 7554 SCANTRONFORM
|
Line 7441 SCANTRONFORM
|
'inline',undef,'scantronupload'); |
'inline',undef,'scantronupload'); |
&Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state, |
&Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state, |
'Processing first student'); |
'Processing first student'); |
$r->print('<br />'); |
|
my $start=&Time::HiRes::time(); |
my $start=&Time::HiRes::time(); |
my $i=-1; |
my $i=-1; |
my $started; |
my ($uname,$udom,$started); |
|
|
&scantron_get_maxbubble(); # Need the bubble lines array to parse. |
&scantron_get_maxbubble(); # Need the bubble lines array to parse. |
|
|
Line 7573 SCANTRONFORM
|
Line 7459 SCANTRONFORM
|
return ''; # Dunno why the other returns return '' rather than just returning. |
return ''; # Dunno why the other returns return '' rather than just returning. |
} |
} |
|
|
my %lettdig = &letter_to_digits(); |
|
my $numletts = scalar(keys(%lettdig)); |
|
|
|
while ($i<$scanlines->{'count'}) { |
while ($i<$scanlines->{'count'}) { |
($uname,$udom)=('',''); |
($uname,$udom)=('',''); |
$i++; |
$i++; |
Line 7607 SCANTRONFORM
|
Line 7490 SCANTRONFORM
|
if (&scantron_clear_skip($scanlines,$scan_data,$i)) { |
if (&scantron_clear_skip($scanlines,$scan_data,$i)) { |
&scantron_putfile($scanlines,$scan_data); |
&scantron_putfile($scanlines,$scan_data); |
} |
} |
|
|
|
my $i=0; |
|
foreach my $resource (@resources) { |
|
$i++; |
|
my %form=('submitted' =>'scantron', |
|
'grade_target' =>'grade', |
|
'grade_username'=>$uname, |
|
'grade_domain' =>$udom, |
|
'grade_courseid'=>$env{'request.course.id'}, |
|
'grade_symb' =>$resource->symb()); |
|
if (exists($scan_record->{'scantron.CODE'}) |
|
&& |
|
&Apache::lonnet::validCODE($scan_record->{'scantron.CODE'})) { |
|
$form{'CODE'}=$scan_record->{'scantron.CODE'}; |
|
} else { |
|
$form{'CODE'}=''; |
|
} |
|
my $result=&ssi_with_retries($resource->src(), $ssi_retries, %form); |
|
if ($ssi_error) { |
|
$ssi_error = 0; # So end of handler error message does not trigger. |
|
$r->print("</form>"); |
|
&ssi_print_error($r); |
|
$r->print(&show_grading_menu_form($symb)); |
|
&Apache::lonnet::remove_lock($lock); |
|
return ''; # Why return ''? Beats me. |
|
} |
|
|
my $scancode; |
if (&Apache::loncommon::connection_aborted($r)) { last; } |
if ((exists($scan_record->{'scantron.CODE'})) && |
} |
(&Apache::lonnet::validCODE($scan_record->{'scantron.CODE'}))) { |
|
$scancode = $scan_record->{'scantron.CODE'}; |
|
} else { |
|
$scancode = ''; |
|
} |
|
|
|
if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode, |
|
@resources) eq 'ssi_error') { |
|
$ssi_error = 0; # So end of handler error message does not trigger. |
|
$r->print("</form>"); |
|
&ssi_print_error($r); |
|
$r->print(&show_grading_menu_form($symb)); |
|
&Apache::lonnet::remove_lock($lock); |
|
return ''; # Why return ''? Beats me. |
|
} |
|
|
|
$completedstudents{$uname}={'line'=>$line}; |
$completedstudents{$uname}={'line'=>$line}; |
if ($env{'form.verifyrecord'}) { |
|
my $lastpos = $env{'form.scantron_maxbubble'}*$scantron_config{'Qlength'}; |
|
my $studentdata = substr($line,$scantron_config{'Qstart'}-1,$lastpos); |
|
chomp($studentdata); |
|
$studentdata =~ s/\r$//; |
|
my $studentrecord = ''; |
|
my $counter = -1; |
|
foreach my $resource (@resources) { |
|
($counter,my $recording) = |
|
&verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'}, |
|
$counter,$studentdata,\%partids_by_symb, |
|
\%scantron_config,\%lettdig,$numletts); |
|
$studentrecord .= $recording; |
|
} |
|
if ($studentrecord ne $studentdata) { |
|
$counter = -1; |
|
$studentrecord = ''; |
|
foreach my $resource (@resources) { |
|
($counter,my $recording) = |
|
&verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'}, |
|
$counter,$studentdata,\%partids_by_symb, |
|
\%scantron_config,\%lettdig,$numletts); |
|
$studentrecord .= $recording; |
|
} |
|
if ($studentrecord ne $studentdata) { |
|
$r->print('<p><span class="LC_error">'); |
|
if ($scancode eq '') { |
|
$r->print(&mt('Mismatch grading bubble sheet for user: [_1] with ID: [_2].', |
|
$uname.':'.$udom,$scan_record->{'scantron.ID'})); |
|
} else { |
|
$r->print(&mt('Mismatch grading bubble sheet for user: [_1] with ID: [_2] and CODE: [_3].', |
|
$uname.':'.$udom,$scan_record->{'scantron.ID'},$scancode)); |
|
} |
|
$r->print('</span><br />'.&Apache::loncommon::start_data_table()."\n". |
|
&Apache::loncommon::start_data_table_header_row()."\n". |
|
'<th>'.&mt('Source').'</th><th>'.&mt('Bubbled responses').'</th>'. |
|
&Apache::loncommon::end_data_table_header_row()."\n". |
|
&Apache::loncommon::start_data_table_row(). |
|
'<td>'.&mt('Bubble Sheet').'</td>'. |
|
'<td><span class="LC_nobreak">'.$studentdata.'</span></td>'. |
|
&Apache::loncommon::end_data_table_row(). |
|
&Apache::loncommon::start_data_table_row(). |
|
'<td>Stored submissions</td>'. |
|
'<td><span class="LC_nobreak">'.$studentrecord.'</span></td>'."\n". |
|
&Apache::loncommon::end_data_table_row(). |
|
&Apache::loncommon::end_data_table().'</p>'); |
|
} else { |
|
$r->print('<br /><span class="LC_warning">'. |
|
&mt('A second grading pass was needed for user: [_1] with ID: [_2], because a mismatch was seen on the first pass.',$uname.':'.$udom,$scan_record->{'scantron.ID'}).'<br />'. |
|
&mt("As a consequence, this user's submission history records two tries."). |
|
'</span><br />'); |
|
} |
|
} |
|
} |
|
if (&Apache::loncommon::connection_aborted($r)) { last; } |
if (&Apache::loncommon::connection_aborted($r)) { last; } |
} continue { |
} continue { |
&Apache::lonxml::clear_problem_counter(); |
&Apache::lonxml::clear_problem_counter(); |
Line 7696 SCANTRONFORM
|
Line 7535 SCANTRONFORM
|
return ''; |
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)); |
|
} |
|
return; |
|
} |
|
|
|
=pod |
|
|
|
=item scantron_upload_scantron_data |
|
|
|
Creates the screen for adding a new bubble sheet data file to a course. |
|
|
|
=cut |
|
|
|
sub scantron_upload_scantron_data { |
sub scantron_upload_scantron_data { |
my ($r)=@_; |
my ($r)=@_; |
$r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'})); |
$r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'})); |
Line 7761 sub scantron_upload_scantron_data {
|
Line 7575 sub scantron_upload_scantron_data {
|
return ''; |
return ''; |
} |
} |
|
|
=pod |
|
|
|
=item scantron_upload_scantron_data_save |
|
|
|
Adds a provided bubble information data file to the course if user |
|
has the correct privileges to do so. |
|
|
|
=cut |
|
|
|
sub scantron_upload_scantron_data_save { |
sub scantron_upload_scantron_data_save { |
my($r)=@_; |
my($r)=@_; |
Line 7830 sub scantron_upload_scantron_data_save {
|
Line 7636 sub scantron_upload_scantron_data_save {
|
return ''; |
return ''; |
} |
} |
|
|
=pod |
|
|
|
=item valid_file |
|
|
|
Validates that the requested bubble data file exists in the course. |
|
|
|
=cut |
|
|
|
sub valid_file { |
sub valid_file { |
my ($requested_file)=@_; |
my ($requested_file)=@_; |
foreach my $filename (sort(&scantron_filenames())) { |
foreach my $filename (sort(&scantron_filenames())) { |
Line 7846 sub valid_file {
|
Line 7644 sub valid_file {
|
return 0; |
return 0; |
} |
} |
|
|
=pod |
|
|
|
=item scantron_download_scantron_data |
|
|
|
Shows a list of the three internal files (original, corrected, |
|
skipped) for a specific bubble sheet data file that exists in the |
|
course. |
|
|
|
=cut |
|
|
|
sub scantron_download_scantron_data { |
sub scantron_download_scantron_data { |
my ($r)=@_; |
my ($r)=@_; |
my $default_form_data=&defaultFormData(&get_symb($r,1)); |
my $default_form_data=&defaultFormData(&get_symb($r,1)); |
Line 7901 sub checkscantron_results {
|
Line 7689 sub checkscantron_results {
|
if (!$symb) {return '';} |
if (!$symb) {return '';} |
my $grading_menu_button=&show_grading_menu_form($symb); |
my $grading_menu_button=&show_grading_menu_form($symb); |
my $cid = $env{'request.course.id'}; |
my $cid = $env{'request.course.id'}; |
my %lettdig = &letter_to_digits(); |
my %lettdig = ( |
|
A => 1, |
|
B => 2, |
|
C => 3, |
|
D => 4, |
|
E => 5, |
|
F => 6, |
|
G => 7, |
|
H => 8, |
|
I => 9, |
|
J => 0, |
|
); |
my $numletts = scalar(keys(%lettdig)); |
my $numletts = scalar(keys(%lettdig)); |
my $cnum = $env{'course.'.$cid.'.num'}; |
my $cnum = $env{'course.'.$cid.'.num'}; |
my $cdom = $env{'course.'.$cid.'.domain'}; |
my $cdom = $env{'course.'.$cid.'.domain'}; |
Line 7915 sub checkscantron_results {
|
Line 7714 sub checkscantron_results {
|
my $navmap=Apache::lonnavmaps::navmap->new(); |
my $navmap=Apache::lonnavmaps::navmap->new(); |
my $map=$navmap->getResourceByUrl($sequence); |
my $map=$navmap->getResourceByUrl($sequence); |
my @resources=$navmap->retrieveResources($map,undef,1,0); |
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; |
|
} |
|
my (%scandata,%lastname,%bylast); |
my (%scandata,%lastname,%bylast); |
$r->print(' |
$r->print(' |
<form method="post" enctype="multipart/form-data" action="/adm/grades" name="checkscantron">'."\n"); |
<form method="post" enctype="multipart/form-data" action="/adm/grades" name="checkscantron">'."\n"); |
Line 7933 sub checkscantron_results {
|
Line 7725 sub checkscantron_results {
|
my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron/Submissions Comparison Status', |
my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron/Submissions Comparison Status', |
'Progress of Scantron Data/Submission Records Comparison',$count, |
'Progress of Scantron Data/Submission Records Comparison',$count, |
'inline',undef,'checkscantron'); |
'inline',undef,'checkscantron'); |
my ($username,$domain,$started); |
my ($username,$domain,$uname,$started); |
|
|
&Apache::grades::scantron_get_maxbubble(); # Need the bubble lines array to parse. |
&Apache::grades::scantron_get_maxbubble(); # Need the bubble lines array to parse. |
|
|
Line 7975 sub checkscantron_results {
|
Line 7767 sub checkscantron_results {
|
$scandata{$pid} =~ s/\r$//; |
$scandata{$pid} =~ s/\r$//; |
($username,$domain)=split(/:/,$uname); |
($username,$domain)=split(/:/,$uname); |
my $counter = -1; |
my $counter = -1; |
|
my (%expected,%startpos); |
foreach my $resource (@resources) { |
foreach my $resource (@resources) { |
($counter,my $recording) = |
next if (!$resource->is_problem()); |
&verify_scantron_grading($resource,$domain,$username,$cid,$counter, |
my $symb = $resource->symb(); |
$scandata{$pid},\%partids_by_symb, |
my $partsref = $resource->parts(); |
\%scantron_config,\%lettdig,$numletts); |
my @parts; |
$record{$pid} .= $recording; |
my @part_ids = (); |
|
if (ref($partsref) eq 'ARRAY') { |
|
@parts = @{$partsref}; |
|
foreach my $part (@parts) { |
|
my @resp_ids = $resource->responseIds($part); |
|
foreach my $resp (@resp_ids) { |
|
$counter ++; |
|
my $part_id = $part.'.'.$resp; |
|
$expected{$part_id} = 0; |
|
push(@part_ids,$part_id); |
|
if ($env{"form.scantron.sub_bubblelines.$counter"}) { |
|
my @sub_lines = split(/,/,$env{"form.scantron.sub_bubblelines.$counter"}); |
|
foreach my $item (@sub_lines) { |
|
$expected{$part_id} += $item; |
|
} |
|
} else { |
|
$expected{$part_id} = $env{"form.scantron.bubblelines.$counter"}; |
|
} |
|
$startpos{$part_id} = $env{"form.scantron.first_bubble_line.$counter"}; |
|
} |
|
} |
|
} |
|
if ($symb) { |
|
my %recorded; |
|
my (%returnhash) = |
|
&Apache::lonnet::restore($symb,$cid,$domain,$username); |
|
if ($returnhash{'version'}) { |
|
my %lasthash=(); |
|
my $version; |
|
for ($version=1;$version<=$returnhash{'version'};$version++) { |
|
foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) { |
|
$lasthash{$key}=$returnhash{$version.':'.$key}; |
|
} |
|
} |
|
foreach my $key (keys(%lasthash)) { |
|
if ($key =~ /\.scantron$/) { |
|
my $value = &unescape($lasthash{$key}); |
|
my ($part_id) = ($key =~ /^resource\.(.+)\.scantron$/); |
|
if ($value eq '') { |
|
for (my $i=0; $i<$expected{$part_id}; $i++) { |
|
for (my $j=0; $j<$scantron_config{'length'}; $j++) { |
|
$recorded{$part_id} .= $; |
|
} |
|
} |
|
} else { |
|
my @tocheck; |
|
my @items = split(//,$value); |
|
if (($scantron_config{'Qon'} eq 'letter') || |
|
($scantron_config{'Qon'} eq 'number')) { |
|
if (@items < $expected{$part_id}) { |
|
my $fragment = substr($scandata{$pid},$startpos{$part_id},$expected{$part_id}); |
|
my @singles = split(//,$fragment); |
|
foreach my $pos (@singles) { |
|
if ($pos eq ' ') { |
|
push(@tocheck,$pos); |
|
} else { |
|
my $next = shift(@items); |
|
push(@tocheck,$next); |
|
} |
|
} |
|
} else { |
|
@tocheck = @items; |
|
} |
|
foreach my $letter (@tocheck) { |
|
if ($scantron_config{'Qon'} eq 'letter') { |
|
if ($letter !~ /^[A-J]$/) { |
|
$letter = $scantron_config{'Qoff'}; |
|
} |
|
$recorded{$part_id} .= $letter; |
|
} elsif ($scantron_config{'Qon'} eq 'number') { |
|
my $digit; |
|
if ($letter !~ /^[A-J]$/) { |
|
$digit = $scantron_config{'Qoff'}; |
|
} else { |
|
$digit = $lettdig{$letter}; |
|
} |
|
$recorded{$part_id} .= $digit; |
|
} |
|
} |
|
} else { |
|
@tocheck = @items; |
|
for (my $i=0; $i<$expected{$part_id}; $i++) { |
|
my $curr_sub = shift(@tocheck); |
|
my $digit; |
|
if ($curr_sub =~ /^[A-J]$/) { |
|
$digit = $lettdig{$curr_sub}-1; |
|
} |
|
if ($curr_sub eq 'J') { |
|
$digit += scalar($numletts); |
|
} |
|
for (my $j=0; $j<$scantron_config{'Qlength'}; $j++) { |
|
if ($j == $digit) { |
|
$recorded{$part_id} .= $scantron_config{'Qon'}; |
|
} else { |
|
$recorded{$part_id} .= $scantron_config{'Qoff'}; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
foreach my $part_id (@part_ids) { |
|
if ($recorded{$part_id} eq '') { |
|
for (my $i=0; $i<$expected{$part_id}; $i++) { |
|
for (my $j=0; $j<$scantron_config{'Qlength'}; $j++) { |
|
$recorded{$part_id} .= $scantron_config{'Qoff'}; |
|
} |
|
} |
|
} |
|
$record{$pid} .= $recorded{$part_id}; |
|
} |
|
} |
} |
} |
} |
} |
&Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); |
&Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); |
Line 8042 sub checkscantron_results {
|
Line 7948 sub checkscantron_results {
|
return; |
return; |
} |
} |
|
|
sub verify_scantron_grading { |
|
my ($resource,$domain,$username,$cid,$counter,$scandata,$partids_by_symb, |
|
$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}}) { |
|
$counter ++; |
|
$expected{$part_id} = 0; |
|
if ($env{"form.scantron.sub_bubblelines.$counter"}) { |
|
my @sub_lines = split(/,/,$env{"form.scantron.sub_bubblelines.$counter"}); |
|
foreach my $item (@sub_lines) { |
|
$expected{$part_id} += $item; |
|
} |
|
} else { |
|
$expected{$part_id} = $env{"form.scantron.bubblelines.$counter"}; |
|
} |
|
$startpos{$part_id} = $env{"form.scantron.first_bubble_line.$counter"}; |
|
} |
|
if ($symb) { |
|
my %recorded; |
|
my (%returnhash) = &Apache::lonnet::restore($symb,$cid,$domain,$username); |
|
if ($returnhash{'version'}) { |
|
my %lasthash=(); |
|
my $version; |
|
for ($version=1;$version<=$returnhash{'version'};$version++) { |
|
foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) { |
|
$lasthash{$key}=$returnhash{$version.':'.$key}; |
|
} |
|
} |
|
foreach my $key (keys(%lasthash)) { |
|
if ($key =~ /\.scantron$/) { |
|
my $value = &unescape($lasthash{$key}); |
|
my ($part_id) = ($key =~ /^resource\.(.+)\.scantron$/); |
|
if ($value eq '') { |
|
for (my $i=0; $i<$expected{$part_id}; $i++) { |
|
for (my $j=0; $j<$scantron_config->{'length'}; $j++) { |
|
$recorded{$part_id} .= $scantron_config->{'Qoff'}; |
|
} |
|
} |
|
} else { |
|
my @tocheck; |
|
my @items = split(//,$value); |
|
if (($scantron_config->{'Qon'} eq 'letter') || |
|
($scantron_config->{'Qon'} eq 'number')) { |
|
if (@items < $expected{$part_id}) { |
|
my $fragment = substr($scandata,$startpos{$part_id},$expected{$part_id}); |
|
my @singles = split(//,$fragment); |
|
foreach my $pos (@singles) { |
|
if ($pos eq ' ') { |
|
push(@tocheck,$pos); |
|
} else { |
|
my $next = shift(@items); |
|
push(@tocheck,$next); |
|
} |
|
} |
|
} else { |
|
@tocheck = @items; |
|
} |
|
foreach my $letter (@tocheck) { |
|
if ($scantron_config->{'Qon'} eq 'letter') { |
|
if ($letter !~ /^[A-J]$/) { |
|
$letter = $scantron_config->{'Qoff'}; |
|
} |
|
$recorded{$part_id} .= $letter; |
|
} elsif ($scantron_config->{'Qon'} eq 'number') { |
|
my $digit; |
|
if ($letter !~ /^[A-J]$/) { |
|
$digit = $scantron_config->{'Qoff'}; |
|
} else { |
|
$digit = $lettdig->{$letter}; |
|
} |
|
$recorded{$part_id} .= $digit; |
|
} |
|
} |
|
} else { |
|
@tocheck = @items; |
|
for (my $i=0; $i<$expected{$part_id}; $i++) { |
|
my $curr_sub = shift(@tocheck); |
|
my $digit; |
|
if ($curr_sub =~ /^[A-J]$/) { |
|
$digit = $lettdig->{$curr_sub}-1; |
|
} |
|
if ($curr_sub eq 'J') { |
|
$digit += scalar($numletts); |
|
} |
|
for (my $j=0; $j<$scantron_config->{'Qlength'}; $j++) { |
|
if ($j == $digit) { |
|
$recorded{$part_id} .= $scantron_config->{'Qon'}; |
|
} else { |
|
$recorded{$part_id} .= $scantron_config->{'Qoff'}; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
foreach my $part_id (@{$partids_by_symb->{$symb}}) { |
|
if ($recorded{$part_id} eq '') { |
|
for (my $i=0; $i<$expected{$part_id}; $i++) { |
|
for (my $j=0; $j<$scantron_config->{'Qlength'}; $j++) { |
|
$recorded{$part_id} .= $scantron_config->{'Qoff'}; |
|
} |
|
} |
|
} |
|
$record .= $recorded{$part_id}; |
|
} |
|
} |
|
return ($counter,$record); |
|
} |
|
|
|
sub letter_to_digits { |
|
my %lettdig = ( |
|
A => 1, |
|
B => 2, |
|
C => 3, |
|
D => 4, |
|
E => 5, |
|
F => 6, |
|
G => 7, |
|
H => 8, |
|
I => 9, |
|
J => 0, |
|
); |
|
return %lettdig; |
|
} |
|
|
|
=pod |
|
|
|
=back |
|
|
|
=cut |
|
|
|
#-------- end of section for handling grading scantron forms ------- |
#-------- end of section for handling grading scantron forms ------- |
# |
# |
Line 8381 GRADINGMENUJS
|
Line 8150 GRADINGMENUJS
|
my $saveSub = ($$savedState{'saveSub'} eq '' ? 'all' : $$savedState{'saveSub'}); |
my $saveSub = ($$savedState{'saveSub'} eq '' ? 'all' : $$savedState{'saveSub'}); |
my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'}); |
my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'}); |
|
|
|
# Preselect sections |
|
my $selsec=""; |
|
if (ref($sections)) { |
|
foreach my $section (sort(@$sections)) { |
|
$selsec.='<option value="'.$section.'" '. |
|
($saveSec eq $section ? 'selected="selected"':'').'>'.$section.'</option>'."\n"; |
|
} |
|
} |
|
|
$result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n". |
$result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n". |
'<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n". |
'<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n". |
'<input type="hidden" name="handgrade" value="'.$hdgrade.'" />'."\n". |
'<input type="hidden" name="handgrade" value="'.$hdgrade.'" />'."\n". |
Line 8391 GRADINGMENUJS
|
Line 8169 GRADINGMENUJS
|
'<input type="hidden" name="showgrading" value="yes" />'."\n"; |
'<input type="hidden" name="showgrading" value="yes" />'."\n"; |
|
|
$result.=' |
$result.=' |
<div class="LC_grade_select_mode"> |
<h2> |
<div class="LC_grade_select_mode_current"> |
'.&mt('Grade Current Resource').' |
<h2> |
</h2> |
'.&mt('Grade Current Resource').' |
<div> |
</h2> |
'.$table.' |
<div class="LC_grade_select_mode_body"> |
</div> |
<div class="LC_grades_resource_info"> |
|
'.$table.' |
<div class="columnSection"> |
</div> |
<div> |
<div class="LC_grade_select_mode_selector"> |
<fieldset> |
<div class="LC_grade_select_mode_selector_header"> |
<legend> |
'.&mt('Sections').' |
'.&mt('Sections').' |
</div> |
</legend> |
<div class="LC_grade_select_mode_selector_body"> |
<select name="section" multiple="multiple" size="5">'."\n"; |
<select name="section" multiple="multiple" size="5">'."\n"; |
$result.= $selsec; |
if (ref($sections)) { |
|
foreach my $section (sort(@$sections)) { |
|
$result.='<option value="'.$section.'" '. |
|
($saveSec eq $section ? 'selected="selected"':'').'>'.$section.'</option>'."\n"; |
|
} |
|
} |
|
$result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="selected"' : ''). '>all</option></select> '; |
$result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="selected"' : ''). '>all</option></select> '; |
$result.=' |
$result.=' |
</div> |
</fieldset> |
</div> |
</div> |
<div class="LC_grade_select_mode_selector"> |
|
<div class="LC_grade_select_mode_selector_header"> |
<div> |
'.&mt('Groups').' |
<fieldset> |
</div> |
<legend> |
<div class="LC_grade_select_mode_selector_body"> |
'.&mt('Groups').' |
'.&Apache::lonstatistics::GroupSelect('group','multiple',5).' |
</legend> |
</div> |
'.&Apache::lonstatistics::GroupSelect('group','multiple',5).' |
</div> |
</fieldset> |
<div class="LC_grade_select_mode_selector"> |
</div> |
<div class="LC_grade_select_mode_selector_header"> |
|
'.&mt('Access Status').' |
<div> |
</div> |
<fieldset> |
<div class="LC_grade_select_mode_selector_body"> |
<legend> |
'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,5,undef,'mult').' |
'.&mt('Access Status').' |
</div> |
</legend> |
</div> |
'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,5,undef,'mult').' |
<div class="LC_grade_select_mode_selector"> |
</fieldset> |
<div class="LC_grade_select_mode_selector_header"> |
</div> |
'.&mt('Submission Status').' |
|
</div> |
<div> |
<div class="LC_grade_select_mode_selector_body"> |
<fieldset> |
<select name="submitonly" size="5"> |
<legend> |
|
'.&mt('Submission Status').' |
|
</legend> |
|
<select name="submitonly" size="5"> |
<option value="yes" '. ($saveSub eq 'yes' ? 'selected="selected"' : '').'>'.&mt('with submissions').'</option> |
<option value="yes" '. ($saveSub eq 'yes' ? 'selected="selected"' : '').'>'.&mt('with submissions').'</option> |
<option value="queued" '. ($saveSub eq 'queued' ? 'selected="selected"' : '').'>'.&mt('in grading queue').'</option> |
<option value="queued" '. ($saveSub eq 'queued' ? 'selected="selected"' : '').'>'.&mt('in grading queue').'</option> |
<option value="graded" '. ($saveSub eq 'graded' ? 'selected="selected"' : '').'>'.&mt('with ungraded submissions').'</option> |
<option value="graded" '. ($saveSub eq 'graded' ? 'selected="selected"' : '').'>'.&mt('with ungraded submissions').'</option> |
<option value="incorrect" '.($saveSub eq 'incorrect' ? 'selected="selected"' : '').'>'.&mt('with incorrect submissions').'</option> |
<option value="incorrect" '.($saveSub eq 'incorrect' ? 'selected="selected"' : '').'>'.&mt('with incorrect submissions').'</option> |
<option value="all" '. ($saveSub eq 'all' ? 'selected="selected"' : '').'>'.&mt('with any status').'</option> |
<option value="all" '. ($saveSub eq 'all' ? 'selected="selected"' : '').'>'.&mt('with any status').'</option> |
</select> |
</select> |
</div> |
</fieldset> |
</div> |
</div> |
<div class="LC_grade_select_mode_type_body"> |
</div> |
<div class="LC_grade_select_mode_type"> |
|
|
<br /> |
|
<div> |
|
<div> |
<label> |
<label> |
<input type="radio" name="radioChoice" value="submission" '. |
<input type="radio" name="radioChoice" value="submission" '. |
($saveCmd eq 'submission' ? 'checked="checked"' : '').' /> '. |
($saveCmd eq 'submission' ? 'checked="checked"' : '').' /> '. |
&mt('Select individual students to grade and view submissions.').' |
&mt('Select individual students to grade and view submissions.').' |
</label> |
</label> |
</div> |
</div> |
<div class="LC_grade_select_mode_type"> |
<div> |
<label> |
<label> |
<input type="radio" name="radioChoice" value="viewgrades" '. |
<input type="radio" name="radioChoice" value="viewgrades" '. |
($saveCmd eq 'viewgrades' ? 'checked="checked"' : '').' /> '. |
($saveCmd eq 'viewgrades' ? 'checked="checked"' : '').' /> '. |
&mt('Grade all selected students in a grading table.').' |
&mt('Grade all selected students in a grading table.').' |
</label> |
</label> |
</div> |
</div> |
<div class="LC_grade_select_mode_type"> |
<div> |
<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next->').'" /> |
<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next->').'" /> |
</div> |
</div> |
</div> |
</div> |
</div> |
|
</div> |
|
<div class="LC_grade_select_mode_page"> |
|
<h2> |
<h2> |
'.&mt('Grade Complete Folder for One Student').' |
'.&mt('Grade Complete Folder for One Student').' |
</h2> |
</h2> |
<div class="LC_grades_select_mode_body"> |
<div> |
<div class="LC_grade_select_mode_type_body"> |
<div> |
<div class="LC_grade_select_mode_type"> |
|
<label> |
<label> |
<input type="radio" name="radioChoice" value="pickStudentPage" '. |
<input type="radio" name="radioChoice" value="pickStudentPage" '. |
($saveCmd eq 'pickStudentPage' ? 'checked="checked"' : '').' /> '. |
($saveCmd eq 'pickStudentPage' ? 'checked="checked"' : '').' /> '. |
&mt('The <b>complete</b> page/sequence/folder: For one student').' |
&mt('The <b>complete</b> page/sequence/folder: For one student').' |
</label> |
</label> |
</div> |
</div> |
<div class="LC_grade_select_mode_type"> |
<div> |
<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next->').'" /> |
<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next->').'" /> |
</div> |
</div> |
</div> |
|
</div> |
</div> |
</div> |
|
</div> |
|
</form>'; |
</form>'; |
$result .= &show_grading_menu_form($symb); |
$result .= &show_grading_menu_form($symb); |
return $result; |
return $result; |
Line 9159 sub handler {
|
Line 8932 sub handler {
|
1; |
1; |
|
|
__END__; |
__END__; |
|
|
|
|
|
=head1 NAME |
|
|
|
Apache::grades |
|
|
|
=head1 SYNOPSIS |
|
|
|
Handles the viewing of grades. |
|
|
|
This is part of the LearningOnline Network with CAPA project |
|
described at http://www.lon-capa.org. |
|
|
|
=head1 OVERVIEW |
|
|
|
Do an ssi with retries: |
|
While I'd love to factor out this with the vesrion in lonprintout, |
|
that would either require a data coupling between modules, which I refuse to perpetuate (there's quite enough of that already), or would require the invention of another infrastructure |
|
I'm not quite ready to invent (e.g. an ssi_with_retry object). |
|
|
|
At least the logic that drives this has been pulled out into loncommon. |
|
|
|
|
|
|
|
ssi_with_retries - Does the server side include of a resource. |
|
if the ssi call returns an error we'll retry it up to |
|
the number of times requested by the caller. |
|
If we still have a proble, no text is appended to the |
|
output and we set some global variables. |
|
to indicate to the caller an SSI error occurred. |
|
All of this is supposed to deal with the issues described |
|
in LonCAPA BZ 5631 see: |
|
http://bugs.lon-capa.org/show_bug.cgi?id=5631 |
|
by informing the user that this happened. |
|
|
|
Parameters: |
|
resource - The resource to include. This is passed directly, without |
|
interpretation to lonnet::ssi. |
|
form - The form hash parameters that guide the interpretation of the resource |
|
|
|
retries - Number of retries allowed before giving up completely. |
|
Returns: |
|
On success, returns the rendered resource identified by the resource parameter. |
|
Side Effects: |
|
The following global variables can be set: |
|
ssi_error - If an unrecoverable error occurred this becomes true. |
|
It is up to the caller to initialize this to false |
|
if desired. |
|
ssi_error_resource - If an unrecoverable error occurred, this is the value |
|
of the resource that could not be rendered by the ssi |
|
call. |
|
ssi_error_message - The error string fetched from the ssi response |
|
in the event of an error. |
|
|
|
|
|
=head1 HANDLER SUBROUTINE |
|
|
|
ssi_with_retries() |
|
|
|
=head1 SUBROUTINES |
|
|
|
=over |
|
|
|
=item scantron_get_correction() : |
|
|
|
Builds the interface screen to interact with the operator to fix a |
|
specific error condition in a specific scanline |
|
|
|
Arguments: |
|
$r - Apache request object |
|
$i - number of the current scanline |
|
$scan_record - hash ref as returned from &scantron_parse_scanline() |
|
$scan_config - hash ref as returned from &get_scantron_config() |
|
$line - full contents of the current scanline |
|
$error - error condition, valid values are |
|
'incorrectCODE', 'duplicateCODE', |
|
'doublebubble', 'missingbubble', |
|
'duplicateID', 'incorrectID' |
|
$arg - extra information needed |
|
For errors: |
|
- duplicateID - paper number that this studentID was seen before on |
|
- duplicateCODE - array ref of the paper numbers this CODE was |
|
seen on before |
|
- incorrectCODE - current incorrect CODE |
|
- doublebubble - array ref of the bubble lines that have double |
|
bubble errors |
|
- missingbubble - array ref of the bubble lines that have missing |
|
bubble errors |
|
|
|
=item scantron_get_maxbubble() : |
|
|
|
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() |
|
for what the current value of the problem counter is. |
|
|
|
Caches the results to $env{'form.scantron_maxbubble'}, |
|
$env{'form.scantron.bubble_lines.n'}, |
|
$env{'form.scantron.first_bubble_line.n'} and |
|
$env{"form.scantron.sub_bubblelines.n"} |
|
which are the total number of bubble, lines, the number of bubble |
|
lines for response n and number of the first bubble line for response n, |
|
and a comma separated list of numbers of bubble lines for sub-questions |
|
(for optionresponse, matchresponse, and rankresponse items), for response n. |
|
|
|
|
|
=item scantron_validate_missingbubbles() : |
|
|
|
Validates all scanlines in the selected file to not have any |
|
answers that don't have bubbles that have not been verified |
|
to be bubble free. |
|
|
|
=item scantron_process_students() : |
|
|
|
Routine that does the actual grading of the bubble sheet information. |
|
|
|
The parsed scanline hash is added to %env |
|
|
|
Then foreach unskipped scanline it does an &Apache::lonnet::ssi() |
|
foreach resource , with the form data of |
|
|
|
'submitted' =>'scantron' |
|
'grade_target' =>'grade', |
|
'grade_username'=> username of student |
|
'grade_domain' => domain of student |
|
'grade_courseid'=> of course |
|
'grade_symb' => symb of resource to grade |
|
|
|
This triggers a grading pass. The problem grading code takes care |
|
of converting the bubbled letter information (now in %env) into a |
|
valid submission. |
|
|
|
=item scantron_upload_scantron_data() : |
|
|
|
Creates the screen for adding a new bubble sheet data file to a course. |
|
|
|
=item scantron_upload_scantron_data_save() : |
|
|
|
Adds a provided bubble information data file to the course if user |
|
has the correct privileges to do so. |
|
|
|
=item valid_file() : |
|
|
|
Validates that the requested bubble data file exists in the course. |
|
|
|
=item scantron_download_scantron_data() : |
|
|
|
Shows a list of the three internal files (original, corrected, |
|
skipped) for a specific bubble sheet data file that exists in the |
|
course. |
|
|
|
=item scantron_validate_ID() : |
|
|
|
Validates all scanlines in the selected file to not have any |
|
invalid or underspecified student IDs |
|
|
|
=back |
|
|
|
=cut |