--- loncom/homework/grades.pm 2010/04/01 00:58:43 1.604 +++ loncom/homework/grades.pm 2013/08/15 12:38:30 1.701 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.604 2010/04/01 00:58:43 raeburn Exp $ +# $Id: grades.pm,v 1.701 2013/08/15 12:38:30 bisitz Exp $ # # Copyright Michigan State University Board of Trustees # @@ -40,9 +40,12 @@ use Apache::lonhomework; use Apache::lonpickcode; use Apache::loncoursedata; use Apache::lonmsg(); -use Apache::Constants qw(:common); +use Apache::Constants qw(:common :http); use Apache::lonlocal; use Apache::lonenc; +use Apache::lonstathelpers; +use Apache::lonquickgrades; +use Apache::bridgetask(); use String::Similarity; use LONCAPA; @@ -51,6 +54,7 @@ use POSIX qw(floor); my %perm=(); +my %old_essays=(); # These variables are used to recover from ssi errors @@ -123,25 +127,6 @@ sub getpartlist { return @stores; } -# --- Get the symbolic name of a problem and the url -# Generate an error message if symb could not be found unless silent flag is set -# Takes $env{'form.symb'} by default; if not present, takes $env{'form.url'} and tries to get symb from that -# - -sub get_symb { - my ($request,$silent) = @_; - (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--; - my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url))); - if ($symb eq '') { - if (!$silent) { - $request->print(&mt("Unable to handle ambiguous references: [_1].",$url)); - return (); - } - } - &Apache::lonenc::check_decrypt(\$symb); - return ($symb); -} - #--- Format fullname, username:domain if different for display #--- Use anywhere where the student names are listed sub nameUserString { @@ -156,6 +141,7 @@ sub nameUserString { #--- Get the partlist and the response type for a given problem. --- #--- Indicate if a response type is coded handgraded or not. --- +#--- Sets response_error pointer to "1" if navmaps object broken --- sub response_type { my ($symb,$response_error) = @_; @@ -217,6 +203,7 @@ sub get_display_part { sub reset_caches { &reset_analyze_cache(); &reset_perm(); + &reset_old_essays(); } { @@ -229,8 +216,13 @@ sub reset_caches { } sub get_analyze { - my ($symb,$uname,$udom,$no_increment,$add_to_hash)=@_; + my ($symb,$uname,$udom,$no_increment,$add_to_hash,$type,$trial,$rndseed,$bubbles_per_row)=@_; my $key = "$symb\0$uname\0$udom"; + if ($type eq 'randomizetry') { + if ($trial ne '') { + $key .= "\0".$trial; + } + } if (exists($analyze_cache{$key})) { my $getupdate = 0; if (ref($add_to_hash) eq 'HASH') { @@ -258,9 +250,18 @@ sub reset_caches { 'grade_courseid' => $env{'request.course.id'}, 'grade_username' => $uname, 'grade_noincrement' => $no_increment); + if ($bubbles_per_row ne '') { + $form{'bubbles_per_row'} = $bubbles_per_row; + } + if ($type eq 'randomizetry') { + $form{'grade_questiontype'} = $type; + if ($rndseed ne '') { + $form{'grade_rndseed'} = $rndseed; + } + } if (ref($add_to_hash)) { %form = (%form,%{$add_to_hash}); - } + } my $subresult=&ssi_with_retries($url, $ssi_retries,%form); (undef,$subresult)=split(/_HASH_REF__/,$subresult,2); my %analyze=&Apache::lonnet::str2hash($subresult); @@ -273,15 +274,15 @@ sub reset_caches { } sub get_order { - my ($partid,$respid,$symb,$uname,$udom,$no_increment)=@_; - my $analyze = &get_analyze($symb,$uname,$udom,$no_increment); + my ($partid,$respid,$symb,$uname,$udom,$no_increment,$type,$trial,$rndseed)=@_; + my $analyze = &get_analyze($symb,$uname,$udom,$no_increment,undef,$type,$trial,$rndseed); return $analyze->{"$partid.$respid.shown"}; } sub get_radiobutton_correct_foil { - my ($partid,$respid,$symb,$uname,$udom)=@_; - my $analyze = &get_analyze($symb,$uname,$udom); - my $foils = &get_order($partid,$respid,$symb,$uname,$udom); + my ($partid,$respid,$symb,$uname,$udom,$type,$trial,$rndseed)=@_; + my $analyze = &get_analyze($symb,$uname,$udom,undef,undef,$type,$trial,$rndseed); + my $foils = &get_order($partid,$respid,$symb,$uname,$udom,undef,$type,$trial,$rndseed); if (ref($foils) eq 'ARRAY') { foreach my $foil (@{$foils}) { if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') { @@ -292,7 +293,7 @@ sub reset_caches { } sub scantron_partids_tograde { - my ($resource,$cid,$uname,$udom,$check_for_randomlist) = @_; + my ($resource,$cid,$uname,$udom,$check_for_randomlist,$bubbles_per_row) = @_; my (%analysis,@parts); if (ref($resource)) { my $symb = $resource->symb(); @@ -300,7 +301,9 @@ sub reset_caches { if ($check_for_randomlist) { $add_to_form = { 'check_parts_withrandomlist' => 1,}; } - my $analyze = &get_analyze($symb,$uname,$udom,undef,$add_to_form); + my $analyze = + &get_analyze($symb,$uname,$udom,undef,$add_to_form, + undef,undef,undef,$bubbles_per_row); if (ref($analyze) eq 'HASH') { %analysis = %{$analyze}; } @@ -323,7 +326,7 @@ sub reset_caches { # response types only. sub cleanRecord { my ($answer,$response,$symb,$partid,$respid,$record,$order,$version, - $uname,$udom) = @_; + $uname,$udom,$type,$trial,$rndseed) = @_; my $grayFont = ''; if ($response =~ /^(option|rank)$/) { my %answer=&Apache::lonnet::str2hash($answer); @@ -340,7 +343,7 @@ sub cleanRecord { return '
'. ''.$toprow.''. ''. - $grayFont.$bottomrow.''.'
'.&mt('Answer').'
'.$grayFont.&mt('Option ID').'
'; + $bottomrow.''; } elsif ($response eq 'match') { my %answer=&Apache::lonnet::str2hash($answer); my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"}); @@ -362,12 +365,12 @@ sub cleanRecord { ''.$grayFont.&mt('Item ID').'
'. $middlerow.''. ''.$grayFont.&mt('Option ID').''. - $bottomrow.''.''; + $bottomrow.''; } elsif ($response eq 'radiobutton') { my %answer=&Apache::lonnet::str2hash($answer); my ($toprow,$bottomrow); my $correct = - &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom); + &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom,$type,$trial,$rndseed); foreach my $foil (@$order) { if (exists($answer{$foil})) { if ($foil eq $correct) { @@ -383,7 +386,7 @@ sub cleanRecord { return '
'. ''.$toprow.''. ''. - $bottomrow.''.'
'.&mt('Answer').'
'.$grayFont.&mt('Option ID').'
'; + $bottomrow.''; } elsif ($response eq 'essay') { if (! exists ($env{'form.'.$symb})) { my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade', @@ -636,8 +639,6 @@ sub jscriptNform { "\n"); $jscript.= '
'."\n". ''."\n". - ''."\n". - ''."\n". ''."\n". ''."\n". ''."\n". @@ -682,7 +683,11 @@ sub compute_points { # sub most_similar { - my ($uname,$udom,$uessay,$old_essays)=@_; + my ($uname,$udom,$symb,$uessay)=@_; + + unless ($symb) { return ''; } + + unless (ref($old_essays{$symb}) eq 'HASH') { return ''; } # ignore spaces and punctuation @@ -699,11 +704,11 @@ sub most_similar { my $scrsid=''; my $sessay=''; # go through all essays ... - foreach my $tkey (keys(%$old_essays)) { + foreach my $tkey (keys(%{$old_essays{$symb}})) { my ($tname,$tdom,$tcrsid)=map {&unescape($_)} (split(/\./,$tkey)); # ... except the same student next if (($tname eq $uname) && ($tdom eq $udom)); - my $tessay=$old_essays->{$tkey}; + my $tessay=$old_essays{$symb}{$tkey}; $tessay=~s/\W+/ /gs; # String similarity gives up if not even limit my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit); @@ -713,7 +718,7 @@ sub most_similar { $sname=$tname; $sdom=$tdom; $scrsid=$tcrsid; - $sessay=$old_essays->{$tkey}; + $sessay=$old_essays{$symb}{$tkey}; } } if ($limit>0.6) { @@ -729,10 +734,9 @@ sub most_similar { # sub initialverifyreceipt { - my $request = shift; + my ($request,$symb) = @_; &commonJSfunctions($request); - my ($symb) = &get_symb($request); - return ''. + return ''. &Apache::lonnet::recprefix($env{'request.course.id'}). '-'. ''."\n". @@ -742,20 +746,17 @@ sub initialverifyreceipt { #--- Check whether a receipt number is valid.--- sub verifyreceipt { - my $request = shift; + my ($request,$symb) = @_; my $courseid = $env{'request.course.id'}; my $receipt = &Apache::lonnet::recprefix($courseid).'-'. $env{'form.receipt'}; $receipt =~ s/[^\-\d]//g; - my ($symb) = &get_symb($request); my $title.= '

'. - &mt('Verifying Receipt No. [_1]',$receipt). - '

'."\n". - '

'.&mt('Resource: [_1]',$env{'form.probTitle'}). - '

'."\n"; + &mt('Verifying Receipt Number [_1]',$receipt). + ''."\n"; my ($string,$contents,$matches) = ('','',0); my (undef,undef,$fullname) = &getclasslist('all','0'); @@ -825,7 +826,7 @@ sub verifyreceipt { $contents. &Apache::loncommon::end_data_table()."\n"; } - return $string.&show_grading_menu_form($symb); + return $string; } #--- This is called by a number of programs. @@ -833,25 +834,19 @@ sub verifyreceipt { #--- Also called directly when one clicks on the subm button # on the problem page. sub listStudents { - my ($request) = shift; + my ($request,$symb,$submitonly) = @_; - my ($symb) = &get_symb($request); my $cdom = $env{"course.$env{'request.course.id'}.domain"}; my $cnum = $env{"course.$env{'request.course.id'}.num"}; my $getsec = $env{'form.section'} eq '' ? 'all' : $env{'form.section'}; my $getgroup = $env{'form.group'} eq '' ? 'all' : $env{'form.group'}; - my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'}; - my $viewgrade = $env{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : '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") - .'

'; + unless ($submitonly) { + $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'}; + } - my ($partlist,$handgrade,$responseType) = &response_type($symb -#,$res_error - ); + my $result=''; + my $res_error; + my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error); my %lt = &Apache::lonlocal::texthash ( 'multiple' => 'Please select a student or group of students before clicking on the Next button.', @@ -891,8 +886,6 @@ LISTJAVASCRIPT &commonJSfunctions($request); $request->print($result); - my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked="checked"' : ''; - my $checklastsub = $checkhdgrade eq '' ? 'checked="checked"' : ''; my $gradeTable=''. "\n"; @@ -909,27 +902,23 @@ LISTJAVASCRIPT .&Apache::lonhtmlcommon::row_closure(); my $submission_options; - if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) { - $submission_options.= - ''."\n"; - } my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status')); my $saveStatus = $stu_status eq '' ? 'Active' : $stu_status; $env{'form.Status'} = $saveStatus; $submission_options.= ''. - ''."\n". + ''."\n". ''. ''."\n". + &mt('last submission with details').' '."\n". ''. - ''."\n". + ''."\n". ''. ''; - $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Submissions')) + &mt('all submissions with details').''; + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Submissions')) .$submission_options .&Apache::lonhtmlcommon::row_closure(); @@ -945,14 +934,10 @@ LISTJAVASCRIPT $gradeTable .= &build_section_inputs(). ''."\n". - '
'."\n". - '
'."\n". - ''."\n". - ''."\n". ''."\n". ''."\n"; - if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) { + if (exists($env{'form.Status'})) { $gradeTable .= ''."\n"; } else { $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status')) @@ -967,7 +952,7 @@ LISTJAVASCRIPT .&Apache::lonhtmlcommon::end_pick_box(); $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" + .&mt("To view/grade/regrade 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" .'' .'

'; @@ -984,9 +969,7 @@ LISTJAVASCRIPT while ($loop < 2) { $gradeTable.=''.&mt('No.').''.&mt('Select').''. ''.&nameUserString('header').' '.&mt('Section/Group').''; - if ($env{'form.showgrading'} eq 'yes' - && $submitonly ne 'queued' - && $submitonly ne 'all') { + if (($submitonly ne 'queued') && ($submitonly ne 'all')) { foreach my $part (sort(@$partlist)) { my $display_part= &get_display_part((split(/_/,$part))[0],$symb); @@ -1022,9 +1005,7 @@ LISTJAVASCRIPT $status{'gradingqueue'} = $queue_status{'gradingqueue'}; } - if ($env{'form.showgrading'} eq 'yes' - && $submitonly ne 'queued' - && $submitonly ne 'all') { + if (($submitonly ne 'queued') && ($submitonly ne 'all')) { (%status) =&student_gradeStatus($symb,$udom,$uname,$partlist); my $submitted = 0; my $graded = 0; @@ -1065,7 +1046,7 @@ LISTJAVASCRIPT &nameUserString(undef,$$fullname{$student},$uname,$udom). ' '.$section.($group ne '' ?'/'.$group:'').''."\n"; - if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { + if ($submitonly ne 'all') { foreach (sort(keys(%status))) { next if ($_ =~ /^resource.*?submitted_by$/); $gradeTable.=' '.&mt($status{$_}).' '."\n"; @@ -1079,9 +1060,7 @@ LISTJAVASCRIPT } if ($ctr%2 ==1) { $gradeTable.='   '; - if ($env{'form.showgrading'} eq 'yes' - && $submitonly ne 'queued' - && $submitonly ne 'all') { + if (($submitonly ne 'queued') && ($submitonly ne 'all')) { foreach (@$partlist) { $gradeTable.=' '; } @@ -1112,7 +1091,6 @@ LISTJAVASCRIPT } elsif ($ctr == 1) { $gradeTable =~ s/type="checkbox"/type="checkbox" checked="checked"/; } - $gradeTable.=&show_grading_menu_form($symb); $request->print($gradeTable); return ''; } @@ -1166,7 +1144,7 @@ sub check_buttons { # Displays the submissions for one student or a group of students sub processGroup { - my ($request) = shift; + my ($request,$symb) = @_; my $ctr = 0; my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo'); my $total = scalar(@stuchecked)-1; @@ -1176,7 +1154,7 @@ sub processGroup { $env{'form.student'} = $uname; $env{'form.userdom'} = $udom; $env{'form.fullname'} = $fullname; - &submission($request,$ctr,$total); + &submission($request,$ctr,$total,$symb); $ctr++; } return ''; @@ -1313,13 +1291,6 @@ sub sub_page_js { } } - if (val == "Grade Student") { - formname.showgrading.value = "yes"; - if (formname.Status.value == "") { - formname.Status.value = "Active"; - } - formname.studentNo.value = total; - } formname.submit(); } @@ -1368,7 +1339,8 @@ sub sub_page_kw_js { my $iconpath = $request->dir_config('lonIconsURL'); &commonJSfunctions($request); - my $inner_js_msg_central= &Apache::lonhtmlcommon::scripttag(< function checkInput() { opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value); var nmsg = opener.document.SCORE.savemsgN.value; @@ -1405,9 +1377,11 @@ sub sub_page_kw_js { self.close() } + INNERJS - my $inner_js_highlight_central= &Apache::lonhtmlcommon::scripttag(< function updateChoice(flag) { opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr); opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize); @@ -1418,6 +1392,7 @@ INNERJS } self.close() } + INNERJS my $start_page_msg_central = @@ -1440,12 +1415,28 @@ INNERJS my $docopen=&Apache::lonhtmlcommon::javascript_docopen(); $docopen=~s/^document\.//; - my $alertmsg = &mt('Please select a word or group of words from document and then click this link.'); + my %lt = &Apache::lonlocal::texthash( + keyw => 'Keywords list, separated by a space. Add/delete to list if desired.', + plse => 'Please select a word or group of words from document and then click this link.', + adds => 'Add selection to keyword list? Edit if desired.', + comp => 'Compose Message for: ', + incl => 'Include', + type => 'Type', + subj => 'Subject', + mesa => 'Message', + new => 'New', + save => 'Save', + canc => 'Cancel', + kehi => 'Keyword Highlight Options', + txtc => 'Text Color', + font => 'Font Size', + fnst => 'Font Style', + ); $request->print(&Apache::lonhtmlcommon::scripttag(< 600) { height = 600; - scrollbar = "yes"; } var xpos = (screen.width-600)/2; xpos = (xpos < 0) ? '0' : xpos; var ypos = (screen.height-height)/2-30; ypos = (ypos < 0) ? '0' : ypos; - pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height); + pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars=yes,screenx='+xpos+',screeny='+ypos+',width=700,height='+height); pWin.focus(); pDoc = pWin.document; pDoc.$docopen; @@ -1557,42 +1546,41 @@ INNERJS pDoc.write(""); pDoc.write(""); - pDoc.write("

 Compose Message for \"+fullname+\"<\\/span><\\/h3>

"); + pDoc.write("

 $lt{'comp'}\"+fullname+\"<\\/h1>"); - pDoc.write('
'); - pDoc.write(''); - pDoc.write("
Type<\\/b><\\/td>Include<\\/b><\\/td>Message<\\/td><\\/tr>"); + pDoc.write(''); + pDoc.write(""); - pDoc.write(""); pDoc.write(""); - pDoc.write(""); pDoc.write("
$lt{'incl'}<\\/b><\\/td>$lt{'type'}<\\/b><\\/td>$lt{'mesa'}<\\/td><\\/tr>"); } function displaySubject(msg,shwsel) { pDoc = pWin.document; - pDoc.write("
Subject<\\/td>"); + pDoc.write("
<\\/td>"); - pDoc.write("<\\/td><\\/tr>"); + pDoc.write("$lt{'subj'}<\\/td>"); + pDoc.write("<\\/td><\\/tr>"); } function displaySavedMsg(ctr,msg,shwsel) { pDoc = pWin.document; - pDoc.write("
"+ctr+"<\\/td>"); + pDoc.write("
<\\/td>"); + pDoc.write(""+ctr+"<\\/td>"); pDoc.write("