--- loncom/homework/grades.pm 2008/06/25 11:59:59 1.525 +++ loncom/homework/grades.pm 2008/12/22 14:55:28 1.541 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.525 2008/06/25 11:59:59 raeburn Exp $ +# $Id: grades.pm,v 1.541 2008/12/22 14:55:28 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,6 +26,8 @@ # http://www.lon-capa.org/ # + + package Apache::grades; use strict; use Apache::style; @@ -58,46 +60,6 @@ my $ssi_error_resource; 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 { my ($resource, $retries, %form) = @_; my ($content, $response) = &Apache::loncommon::ssi_with_retries($resource, $retries, %form); @@ -255,9 +217,9 @@ sub showResourceInfo { $partsseen{$partID}=1; } my $display_part=&get_display_part($partID,$symb); - $result.=''.&mt('Part: [_1]',$display_part).' '. - $resID.''. - ''.&mt('Type: [_1]',$responsetype).''; + $result.=''.&mt('Part').': '.$display_part. + ' '.$resID.''. + ''.&mt('Type').': '.$responsetype.''; # ''.&mt('Handgrade: [_1]',$handgrade).''; } } @@ -817,20 +779,20 @@ sub listStudents { 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' ? 'View/Grade/Regrade' : 'View'; + 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='

 '. - &mt($viewgrade.' Submissions for a Student or a Group of Students') + 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' => - "Please select a student or group of students before clicking on the Next button.", + &mt("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.", + &mt("Please select the student before clicking on the Next button."), ); %lt = &Apache::lonlocal::texthash(%lt); $request->print(<View Problem Text: [_1]', + ' '.&mt('View Problem Text').': '. ''."\n". ''."\n". - '').'
'."\n"; + '
'."\n"; $gradeTable .= - ' '. - &mt('View Answer: [_1]', + ' '.&mt('View Answer').': '. ''."\n". ''."\n". - '').'
'."\n"; + '
'."\n"; my $submission_options; if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) { @@ -901,18 +861,16 @@ LISTJAVASCRIPT ''."\n". ''; $gradeTable .= - ' '. - &mt('Submissions: [_1]',$submission_options).'
'."\n"; + ' '.&mt('Submissions').': '.$submission_options.'
'."\n"; $gradeTable .= - ' '. - &mt('Grading Increments: [_1]', + ' '.&mt('Grading Increments').': '. ''); + ''; $gradeTable .= &build_section_inputs(). @@ -931,15 +889,14 @@ LISTJAVASCRIPT &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);')).'
'; } - $gradeTable.=&mt('To '.lc($viewgrade).' a submission or a group of submissions, click on the check box(es) '. - 'next to the student\'s name(s). Then click on the Next button.').'
'."\n". + $gradeTable.=&mt('To [_1] a submission or a group of submissions, click on the check box(es) next to the student\'s name(s). Then click on the Next button.',lc($viewgrade)).'
'."\n". ''."\n"; # checkall buttons $gradeTable.=&check_script('gradesub', 'stuinfo'); $gradeTable.='
'."\n"; + 'value="'.&mt('Next').' →" />
'."\n"; $gradeTable.=&check_buttons(); $gradeTable.=''; my ($classlist, undef, $fullname) = &getclasslist($getsec,'1',$getgroup); @@ -1059,7 +1016,7 @@ LISTJAVASCRIPT $gradeTable.=&Apache::loncommon::end_data_table()."\n". ''."\n"; + 'value="'.&mt('Next').' →" />'."\n"; if ($ctr == 0) { my $num_students=(scalar(keys(%$fullname))); if ($num_students eq 0) { @@ -1155,6 +1112,7 @@ sub processGroup { #--- Javascript to handle the submission page functionality --- sub sub_page_js { my $request = shift; + my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = '); $request->print(< function updateRadio(formname,id,weight) { @@ -1165,7 +1123,7 @@ sub sub_page_js { gradeBox.value = pts; var resetbox = false; if (isNaN(pts) || pts < 0) { - alert("A number equal or greater than 0 is expected. Entered value = "+pts); + alert("$alertmsg"+pts); for (var i=0; iprint(< @@ -1442,7 +1401,7 @@ INNERJS else return; var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," "); if (cleantxt=="") { - alert("Please select a word or group of words from document and then click this link."); + alert("$alertmsg"); return; } var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt); @@ -1681,7 +1640,7 @@ sub gradeBox { my $radio.=''."\n"; # display radio buttons in a nice table 10 across while ($thisweight<=$wgt) { - $radio.= '\n"; @@ -1697,7 +1656,7 @@ sub gradeBox { $wgt.')" />'."\n"; $line.='',$display_part,$radio,$line); $result .= - &mt('',$display_part,$radio,$line); - + ''. $result.='
/'.$wgt.' '.$wgtmsg. ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? ' '.$checkIcon : ''). - ' '."\n"; + ' '.&mt('Grade Status').':'."\n"; $line.=''."\n"; + #&mt('Part:[_1]Points:[_2]or[_3]Part:[_1]Points:[_2]or[_3]'.&mt('Part').':'.$display_part.''.&mt('Points').':'.$radio.''.&mt('or').''.$line.'
'."\n"; $result.=''."\n". @@ -2094,7 +2053,7 @@ KEYWORDS $lastsubonly.="\n".'
Part: '. $display_part.' ( ID '.$respid. ' )   '. - ''.&mt('Nothing submitted - no attempts').'

'; + ''.&mt('Nothing submitted - no attempts.').'

'; next; } foreach my $submission (@$string) { @@ -2372,7 +2331,7 @@ sub get_last_submission { } if (!@string) { $string[0] = - 'Nothing submitted - no attempts.'; + ''.&mt('Nothing submitted - no attempts.').''; } return (\@string,\$timestamp); } @@ -2796,8 +2755,10 @@ sub handback_files { $newflg.'_'.$part_resp.'_returndoc'.$file_counter, $save_file_name); if ($result !~ m|^/uploaded/|) { - $request->print('An error occurred ('.$result. - ') while trying to upload '.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'
'); + $request->print('
'. + &mt('An error occurred ([_1]) while trying to upload [_2].', + $result,$newflg.'_'.$part_resp.'_returndoc'.$file_counter). + ''); } else { # mark the file as read only my @files = ($save_file_name); @@ -3016,6 +2977,7 @@ sub file_name_version_ext { sub viewgrades_js { my ($request) = shift; + my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = '); $request->print(< function writePoint(partid,weight,point) { @@ -3024,7 +2986,7 @@ sub viewgrades_js { if (point == "textval") { point = document.classgrade["TEXTVAL_"+partid].value; if (isNaN(point) || parseFloat(point) < 0) { - alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point)); + alert("$alertmsg"+parseFloat(point)); var resetbox = false; for (var i=0; i'; + &mt("Assign Common Grade to [_1]",$sectionClass,$section_display).'

'; $result.= &Apache::loncommon::start_data_table(); #radio buttons/text box for assigning points for a section or class. #handles different parts of a problem @@ -3254,8 +3216,8 @@ sub viewgrades { my $line = ' /'. - $weight{$partid}.' (problem weight)'."\n"; - $line.= ' '. ''. @@ -3270,7 +3232,7 @@ sub viewgrades { $result.= &Apache::loncommon::start_data_table_row()."\n". - &mt('Part:[_1]Points:[_2]or[_3]',$display_part,$radio,$line). + ''.&mt('Part').':'.$display_part.''.&mt('Points').':'.$radio.''.&mt('or').''.$line.''. &Apache::loncommon::end_data_table_row()."\n"; $ctsparts++; } @@ -3281,8 +3243,8 @@ sub viewgrades { #table listing all the students in a section/class #header of table - $result.= '

'.&mt('Assign Grade to Specific Students in '.$sectionClass, - $section_display).'

'; + $result.= '

'.&mt('Assign Grade to Specific Students in ').$sectionClass, + $section_display.'

'; $result.= &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(). ''.&mt('No.').''. @@ -3292,7 +3254,8 @@ sub viewgrades { my @partids = (); foreach my $part (@parts) { my $display=&Apache::lonnet::metadata($url,$part.'.display'); - $display =~ s|^Number of Attempts|Tries
|; # makes the column narrower + my $narrowtext = &mt('Tries'); + $display =~ s|^Number of Attempts|$narrowtext
|; # makes the column narrower if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); } my ($partid) = &split_part_type($part); push(@partids,$partid); @@ -3464,9 +3427,10 @@ sub editgrades { if ($type eq 'awarded' || $type eq 'solved') { next; } my $display=&Apache::lonnet::metadata($url,$stores.'.display'); $display =~ s/\[Part: (\w)+\]//; - $display =~ s/Number of Attempts/Tries/; - $header .= ''.&mt('Old '.$display).''. - ''.&mt('New '.$display).''; + my $narrowtext = &mt('Tries'); + $display =~ s/Number of Attempts/$narrowtext/; + $header .= ''.&mt('Old').' '.$display.''. + ''.&mt('New').' '.$display.''; $columns{$partid}+=2; } } @@ -3808,11 +3772,12 @@ ENDPICK } sub checkforfile_js { + my $alertmsg = &mt('Please use the browse button to select a file from your local directory.'); my $result =< function checkUpload(formname) { if (formname.upfile.value == "") { - alert("Please use the browse button to select a file from your local directory."); + alert("$alertmsg"); return false; } formname.submit(); @@ -3832,8 +3797,8 @@ sub upcsvScores_form { $result.=$table; $result.='
'."\n"; $result.=''."\n"; + $result.=' '.&mt('Specify a file containing the class scores for current resource.'). + ''."\n"; $result.='
'."\n"; - $result.=' '.&mt('Specify a file containing the class scores for current resource'). - '.
'."\n"; my $upload=&mt("Upload Scores"); my $upfile_select=&Apache::loncommon::upfile_select_html(); @@ -4086,12 +4051,13 @@ sub csvuploadassign { sub pickStudentPage { my ($request) = shift; + my $alertmsg = &mt('Please select the student you wish to grade.'); $request->print(< function checkPickOne(formname) { if (radioSelection(formname.student) == null) { - alert("Please select the student you wish to grade."); + alert("$alertmsg"); return; } ptr = pullDownSelection(formname.selectpage); @@ -4126,7 +4092,7 @@ LISTJAVASCRIPT $ctr++; } $select.= ''; - $result.=&mt(' Problems from: [_1]',$select)."
\n"; + $result.=' '.&mt('Problems from').': '.$select."
\n"; $ctr=0; foreach (@$titles) { @@ -4141,13 +4107,13 @@ LISTJAVASCRIPT my $options = ''."\n". ''."
\n"; - $result.=' '.&mt('View Problems Text: [_1]',$options); + $result.=' '.&mt('View Problem Text').': '.$options; $options = ''."\n". ''."\n". ''."\n"; - $result.=' '.&mt('Submission Details: [_1]',$options); + $result.=' '.&mt('Submissions').': '.$options; $result.=&build_section_inputs(); my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status')); @@ -4156,12 +4122,10 @@ LISTJAVASCRIPT ''."\n". ''."
\n"; - $result.=' '.&mt('Use CODE: [_1] ', - ''). - '
'."\n"; + $result.=' '.&mt('Use CODE').':
'."\n"; $result.=' 
'."\n"; + 'onClick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' →" />
'."\n"; $request->print($result); @@ -4200,7 +4164,7 @@ LISTJAVASCRIPT } $studentTable.=&Apache::loncommon::end_data_table()."\n"; $studentTable.=''."\n"; + 'onClick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' →" />'."\n"; $studentTable.=&show_grading_menu_form($symb); $request->print($studentTable); @@ -4337,7 +4301,7 @@ sub displayPage { # $request->print('match='.$1."
\n"); # } # $companswer =~ s||
|g; - $studentTable.=' '.$title.' 
 '.&mt('Correct answer:
[_1]',$companswer); + $studentTable.=' '.$title.' 
 '.&mt('Correct answer').':
'.$companswer; } my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname); @@ -4407,7 +4371,7 @@ sub displaySubByDates { my %orders; $mark{'correct_by_student'} = $checkIcon; if (!exists($$record{'1:timestamp'})) { - return '
 '.&mt('Nothing submitted - no attempts').'
'; + return '
 '.&mt('Nothing submitted - no attempts.').'
'; } my $interaction; @@ -4509,12 +4473,12 @@ sub updateGradeByPage { my ($uname,$udom) = split(/:/,$env{'form.student'}); my $usec=$classlist->{$env{'form.student'}}[5]; if (!&canmodify($usec)) { - $request->print('Unable to modify requested student.('.$env{'form.student'}.''); + $request->print(''.&mt('Unable to modify requested student ([_1])',$env{'form.student'}).''); $request->print(&show_grading_menu_form($env{'form.symb'})); return; } my $result='

 '.$env{'form.title'}.'

'; - $result.='

 Student: '.&nameUserString(undef,$env{'form.fullname'},$uname,$udom). + $result.='

 '.&mt('Student: ').&nameUserString(undef,$env{'form.fullname'},$uname,$udom). '

'."\n"; $request->print($result); @@ -4523,7 +4487,7 @@ sub updateGradeByPage { my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $env{'form.page'}); my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps if (!$map) { - $request->print('Unable to grade requested sequence. ('.$resUrl.')'); + $request->print(''.&mt('Unable to grade requested sequence ([_1]).',$resUrl).''); my ($symb)=&get_symb($request); $request->print(&show_grading_menu_form($symb)); return; @@ -4555,8 +4519,8 @@ sub updateGradeByPage { &Apache::loncommon::start_data_table_row(). ''; + : '
('.&mt('[quant,_1, part]',scalar(@{$parts})) + .')').''; $studentTable.=''; my %newrecord=(); @@ -4600,10 +4564,10 @@ sub updateGradeByPage { } my $display_part=&get_display_part($partid,$curRes->symb()); my $oldstatus = $env{'form.solved'.$question.'_'.$partid}; - $displayPts[0].=' Part: '.$display_part.' = '. + $displayPts[0].=' '.&mt('Part').': '.$display_part.' = '. (($oldstatus eq 'excused') ? 'excused' : $oldpts). ' 
'; - $displayPts[1].=' Part: '.$display_part.' = '. + $displayPts[1].=' '.&mt('Part').': '.$display_part.' = '. (($score eq 'excused') ? 'excused' : $newpts). ' 
'; $question++; @@ -4652,9 +4616,9 @@ sub updateGradeByPage { $studentTable.=&Apache::loncommon::end_data_table(); $studentTable.=&show_grading_menu_form($env{'form.symb'}); - my $grademsg=($changeflag == 0 ? 'No score was changed or updated.' : - 'The scores were changed for '. - $changeflag.' problem'.($changeflag == 1 ? '.' : 's.')); + my $grademsg=($changeflag == 0 ? &mt('No score was changed or updated.') : + &mt('The scores were changed for [quant,_1,problem].', + $changeflag)); $request->print($grademsg.$studentTable); return ''; @@ -4985,11 +4949,11 @@ sub scantron_CODElist { =cut sub scantron_CODEunique { - my $result=' + my $result=' - + '; @@ -5149,7 +5113,7 @@ sub scantron_selectphase { &Apache::lonpickcode::code_list($r,2); - $r>print('
'. + $r->print('
'. $default_form_data."\n". &Apache::loncommon::start_data_table('LC_scantron_action')."\n". &Apache::loncommon::start_data_table_header_row()."\n". @@ -6225,15 +6189,15 @@ sub scantron_validate_file { } if ($stop) { if ($validate_phases[$currentphase] eq 'sequence') { - $r->print(''); + $r->print(''); $r->print(' '.&mt('this error').'
'); $r->print("

".&mt("Or click the 'Grading Menu' button to start over.")."

"); } else { if ($validate_phases[$currentphase] eq 'doublebubble' || $validate_phases[$currentphase] eq 'missingbubbles') { - $r->print(''); + $r->print(''); } else { - $r->print(''); + $r->print(''); } $r->print(' '.&mt('using corrected info').'
'); $r->print(""); @@ -6611,14 +6575,7 @@ sub scantron_validate_sequence { 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 { my ($r,$currentphase) = @_; @@ -6684,35 +6641,6 @@ sub scantron_validate_ID { 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 { my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_; @@ -7283,25 +7211,6 @@ sub scantron_validate_doublebubble { 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 { if (defined($env{'form.scantron_maxbubble'}) && @@ -7333,22 +7242,7 @@ sub scantron_get_maxbubble { foreach my $resource (@resources) { my $symb = $resource->symb(); - my (@parts,@allparts,@possible_parts); - - # Need to retrieve part IDs and response IDs because essayresponse, - # 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); - } - } - } - } + my @parts; my $result=&ssi_with_retries($resource->src(), $ssi_retries, ('symb' => $symb, @@ -7369,21 +7263,8 @@ sub scantron_get_maxbubble { } } } - # Add part_ids for any essayresponse, reactionresponse or - # 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); - } - } - } - foreach my $part_id (@allparts) { + foreach my $part_id (@parts) { my $lines; # TODO - make this a persistent hash not an array. @@ -7444,15 +7325,6 @@ sub scantron_get_maxbubble { return $env{'form.scantron_maxbubble'}; } -=pod - -=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. - -=cut sub scantron_validate_missingbubbles { my ($r,$currentphase) = @_; @@ -7507,29 +7379,6 @@ sub scantron_validate_missingbubbles { 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 { my ($r) = @_; @@ -7660,14 +7509,6 @@ SCANTRONFORM 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 { my ($r)=@_; $r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'})); @@ -7681,7 +7522,7 @@ sub scantron_upload_scantron_data {
'.$prob. (scalar(@{$parts}) == 1 ? '' - : '
('.&mt('[quant,_1, parts]',scalar(@{$parts})) - ).')
 '.$title.'