--- loncom/homework/grades.pm 2006/09/22 21:11:55 1.376 +++ loncom/homework/grades.pm 2007/03/14 19:15:37 1.392 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.376 2006/09/22 21:11:55 albertel Exp $ +# $Id: grades.pm,v 1.392 2007/03/14 19:15:37 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -39,6 +39,7 @@ use Apache::loncoursedata; use Apache::lonmsg(); use Apache::Constants qw(:common); use Apache::lonlocal; +use Apache::lonenc; use String::Similarity; use lib '/home/httpd/lib/perl'; use LONCAPA; @@ -112,36 +113,26 @@ sub nameUserString { #--- Indicate if a response type is coded handgraded or not. --- sub response_type { my ($symb) = shift; - my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb); - my $allkeys = &Apache::lonnet::metadata($url,'keys'); - my %vPart; - foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) { - $vPart{$partid}=1; - } - my %seen = (); - my (@partlist,%handgrade,%responseType); - foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) { - if (/^\w+response_.*/ || /^Task_/) { - my ($responsetype,$part) = split(/_/,$_,2); - my ($partid,$respid) = split(/_/,$part,2); - if ($responsetype eq 'Task') { $respid='0'; } - if (&Apache::loncommon::check_if_partid_hidden($partid,$symb)) { - next; - } - if (%vPart && !exists($vPart{$partid})) { - next; - } - $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!! - my ($value) = &Apache::lonnet::EXT('resource.'.$part.'.handgrade',$symb); - $handgrade{$part} = ($value eq 'yes' ? 'yes' : 'no'); - if (!exists($responseType{$partid})) { $responseType{$partid}={}; } - $responseType{$partid}->{$respid}=$responsetype; - next if ($seen{$partid} > 0); - $seen{$partid}++; - push @partlist,$partid; + + my $navmap = Apache::lonnavmaps::navmap->new(); + my $res = $navmap->getBySymb($symb); + my $partlist = $res->parts(); + my %vPart = + map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart')); + my (%response_types,%handgrade); + foreach my $part (@{ $partlist }) { + next if (%vPart && !exists($vPart{$part})); + + my @types = $res->responseType($part); + my @ids = $res->responseIds($part); + for (my $i=0; $i < scalar(@ids); $i++) { + $response_types{$part}{$ids[$i]} = $types[$i]; + $handgrade{$part.'_'.$ids[$i]} = + &Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i]. + '.handgrade',$symb); } } - return (\@partlist,\%handgrade,\%responseType); + return ($partlist,\%handgrade,\%response_types); } sub flatten_responseType { @@ -602,7 +593,7 @@ sub verifyreceipt { my $receipt = &Apache::lonnet::recprefix($courseid).'-'. $env{'form.receipt'}; $receipt =~ s/[^\-\d]//g; - my $symb = &Apache::lonnet::symbread(); + my ($symb) = &get_symb($request); my $title.='

Verifying Submission Receipt '. $receipt.'

'."\n". @@ -612,7 +603,8 @@ sub verifyreceipt { my (undef,undef,$fullname) = &getclasslist('all','0'); my $receiptparts=0; - if ($env{"course.$courseid.receiptalg"} eq 'receipt2') { $receiptparts=1; } + if ($env{"course.$courseid.receiptalg"} eq 'receipt2' || + $env{"course.$courseid.receiptalg"} eq 'receipt3') { $receiptparts=1; } my $parts=['0']; if ($receiptparts) { ($parts)=&response_type($symb); } foreach (sort @@ -1483,7 +1475,8 @@ sub get_increment { #--- displays the grading box, used in essay type problem and grading by page/sequence sub gradeBox { my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_; - my $checkIcon = ''; my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname); my $wgtmsg = ($wgt > 0 ? '(problem weight)' : @@ -1506,10 +1499,10 @@ sub gradeBox { my $increment = &get_increment(); $result.=''."\n"; # display radio buttons in a nice table 10 across while ($thisweight<=$wgt) { - $result.= '\n"; + ($score eq $thisweight ? 'checked':'').' /> '.$thisweight."\n"; $result.=(($ctr+1)%10 == 0 ? '' : ''); $thisweight += $increment; $ctr++; @@ -1526,14 +1519,14 @@ sub gradeBox { $result.=''."\n"; - $result.="  \n"; + $result.="  \n"; $result.=''."\n". ''."\n". 'dir_config('lonIconsURL'). + my $checkIcon = ''.&mt('Check Mark').
+	''; # header info @@ -1766,7 +1764,7 @@ KEYWORDS # Load the other essays for similarity check # my (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb); - my ($adom,$aname,$apath)=($essayurl=~/^(\w+)\/(\w+)\/(.*)$/); + my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/); $apath=&escape($apath); $apath=~s/\W/\_/gs; %oldessays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname); @@ -1918,7 +1916,7 @@ KEYWORDS my $order=&get_order($partid,$respid,$symb,$uname,$udom); if ($env{'form.lastSub'} eq 'lastonly' || ($env{'form.lastSub'} eq 'hdgrade' && - $$handgrade{$part} eq 'yes')) { + $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) { my $display_part=&get_display_part($partid,$symb); $lastsubonly.='
Part: '. $display_part.' ( ID '.$respid. @@ -2124,10 +2122,22 @@ sub processHandGrade { } my $includemsg = $env{'form.includemsg'.$ctr}; my ($subject,$message,$msgstatus) = ('','',''); + my $restitle = &Apache::lonnet::gettitle($symb); + my $encrypturl=&Apache::lonnet::EXT('resource.0.encrypturl', + $symb,$udom,$uname); + my ($feedurl,$baseurl,$showsymb,$messagetail); + $feedurl = &Apache::lonnet::clutter($url); + if ($encrypturl =~ /^yes$/i) { + $baseurl = &Apache::lonenc::encrypted($feedurl,1); + $showsymb = &Apache::lonenc::encrypted($symb,1); + } else { + $baseurl = $feedurl; + $showsymb = $symb; + } if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) { $subject = $env{'form.msgsub'} if ($includemsg =~ /msgsub/); unless ($subject=~/\w/) { $subject=&mt('Grading Feedback'); } - $subject.=' ['.&Apache::lonnet::declutter($url).']'; + $subject.=' ['.$restitle.']'; my (@msgnum) = split(/,/,$includemsg); foreach (@msgnum) { $message.=$env{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne ''); @@ -2135,14 +2145,16 @@ sub processHandGrade { $message =&Apache::lonfeedback::clear_out_html($message); if ($env{'form.withgrades'.$ctr}) { $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt; - $message.=" for $env{'form.probTitle'}"; - } - $msgstatus = &Apache::lonmsg::user_normal_msg($uname,$udom, - $subject, - $message); - $request->print('
'.&mt('Sending message to [_1]@[_2]',$uname,$udom).': '. + $messagetail = " for $env{'form.probTitle'}"; + } + $msgstatus = + &Apache::lonmsg::user_normal_msg($uname,$udom,$subject, + $message.$messagetail, + undef,$baseurl,undef, + undef,undef,$showsymb, + $restitle); + $request->print('
'.&mt('Sending message to [_1]:[_2]',$uname,$udom).': '. $msgstatus); } if ($env{'form.collaborator'.$ctr}) { @@ -2158,7 +2170,23 @@ sub processHandGrade { next; } else { if ($message ne '') { - $msgstatus = &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message); + $encrypturl= + &Apache::lonnet::EXT('resource.0.encrypturl', + $symb,$udom,$collaborator); + if ($encrypturl =~ /^yes$/i) { + $baseurl = &Apache::lonenc::encrypted($feedurl,1); + $showsymb = &Apache::lonenc::encrypted($symb,1); + } else { + $baseurl = $feedurl; + $showsymb = $symb; + } + if ($env{'form.withgrades'.$ctr}) { + $messagetail = " for $env{'form.probTitle'}"; + + } + $msgstatus = + &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle); } } } @@ -2434,17 +2462,8 @@ sub saveHandGrade { } &Apache::lonnet::cstore(\%newrecord,$symb, $env{'request.course.id'},$domain,$stuname); - my @ungraded_parts; - foreach my $part (@parts) { - if ( !defined($record{'resource.'.$part.'.awarded'}) - && !defined($newrecord{'resource.'.$part.'.awarded'}) ) { - push(@ungraded_parts, $part); - } - } - if ( !@ungraded_parts ) { - &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,$cdom, - $cnum,$domain,$stuname); - } + &check_and_remove_from_queue(\@parts,\%record,\%newrecord,$symb, + $cdom,$cnum,$domain,$stuname); } if ($aggregateflag) { &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate, @@ -2453,6 +2472,24 @@ sub saveHandGrade { return ('',$pts,$wgt); } +sub check_and_remove_from_queue { + my ($parts,$record,$newrecord,$symb,$cdom,$cnum,$domain,$stuname) = @_; + my @ungraded_parts; + foreach my $part (@{$parts}) { + if ( $record->{ 'resource.'.$part.'.awarded'} eq '' + && $record->{ 'resource.'.$part.'.solved' } ne 'excused' + && $newrecord->{'resource.'.$part.'.awarded'} eq '' + && $newrecord->{'resource.'.$part.'.solved' } ne 'excused' + ) { + push(@ungraded_parts, $part); + } + } + if ( !@ungraded_parts ) { + &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,$cdom, + $cnum,$domain,$stuname); + } +} + sub handback_files { my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_; my $portfolio_root = &propath($domain,$stuname).'/userfiles/portfolio'; @@ -2505,10 +2542,22 @@ sub handback_files { $message .= ' The returned file(s) are named: '. $file_msg; $message .= " and can be found in your portfolio space."; my $url = (&Apache::lonnet::decode_symb($symb))[2]; - $url = &Apache::lonnet::declutter($url); - my $msgstatus = &Apache::lonmsg::user_normal_msg($stuname,$domain, - $subject.' (File Returned) ['.$url.']',$message); - + my $feedurl = &Apache::lonnet::clutter($url); + my $encrypturl=&Apache::lonnet::EXT('resource.0.encrypturl', + $symb,$domain,$stuname); + my ($baseurl,$showsymb); + if ($encrypturl =~ /^yes$/i) { + $baseurl = &Apache::lonenc::encrypted($feedurl,1); + $showsymb = &Apache::lonenc::encrypted($symb,1); + } else { + $baseurl = $feedurl; + $showsymb = $symb; + } + my $restitle = &Apache::lonnet::gettitle($symb); + my $msgstatus = + &Apache::lonmsg::user_normal_msg($stuname,$domain,$subject. + ' (File Returned) ['.$restitle.']',$message,undef, + $baseurl,undef,undef,undef,$showsymb,$restitle); } } return; @@ -2940,7 +2989,7 @@ sub viewgrades { } $result.='
'.''.''."\n". ''; - $result.=''; #table listing all the students in a section/class @@ -3781,6 +3830,9 @@ LISTJAVASCRIPT ''."\n". ''."
\n"; + $result.=' '.&mt('Use CODE:').' '. + '
'."\n"; + $result.=' 
'."\n"; @@ -3811,8 +3863,8 @@ LISTJAVASCRIPT $studentTable.=($ptr%2 == 0 ? '' : ''); $ptr++; } - $studentTable.='  ' if ($ptr%2 == 0); - $studentTable.=''."\n"; + $studentTable.='  ' if ($ptr%2 == 0); + $studentTable.=''."\n"; $studentTable.=''."\n"; @@ -3835,9 +3887,10 @@ sub getSymbMap { 1,0,1); for my $sequence ($navmap->getById('0.0'), @sequences) { if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) { - my $title = $minder.'.'.$sequence->compTitle(); - push @titles, $title; # minder in case two titles are identical - $symbx{$title} = $sequence->symb(); + my $title = $minder.'.'. + &HTML::Entities::encode($sequence->compTitle(),'"\'&'); + push(@titles, $title); # minder in case two titles are identical + $symbx{$title} = &HTML::Entities::encode($sequence->symb(),'"\'&'); $minder++; } } @@ -3873,6 +3926,11 @@ sub displayPage { my $result='

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

'; $result.='

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

'."\n"; + if (&Apache::lonnet::validCODE($env{'form.CODE'})) { + $result.='

 CODE: '.$env{'form.CODE'}.'

'."\n"; + } else { + delete($env{'form.CODE'}); + } &sub_page_js($request); $request->print($result); @@ -3897,7 +3955,12 @@ sub displayPage { ''."\n". ''."\n"; - my $checkIcon = ''."\n"; + } + my $checkIcon = ''.&mt('Check Mark').
+	''; $studentTable.=' Note: Problems graded correct by the computer are marked with a '.$checkIcon. @@ -3915,18 +3978,19 @@ sub displayPage { if($curRes == $iterator->BEGIN_MAP) { $depth++; } if($curRes == $iterator->END_MAP) { $depth--; } - if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) { + if (ref($curRes) && $curRes->is_problem()) { my $parts = $curRes->parts(); my $title = $curRes->compTitle(); my $symbx = $curRes->symb(); $studentTable.=''.$prob. (scalar(@{$parts}) == 1 ? '' : '
('.scalar(@{$parts}).' parts)').''; $studentTable.=''; + my %form = ('CODE' => $env{'form.CODE'},); if ($env{'form.vProb'} eq 'yes' ) { $studentTable.=&show_problem($request,$symbx,$uname,$udom,1, - undef,'both'); + undef,'both',\%form); } else { - my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'}); + my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form); $companswer =~ s|||g; $companswer =~ s|||g; # while ($companswer =~ /()/s) { #'. + 'onClick="javascript:checkSubmitPage(this.form,'.$question.');" />'. ''."\n"; $studentTable.=&show_grading_menu_form($symb); $request->print($studentTable); @@ -4139,7 +4203,7 @@ sub updateGradeByPage { if($curRes == $iterator->BEGIN_MAP) { $depth++; } if($curRes == $iterator->END_MAP) { $depth--; } - if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) { + if (ref($curRes) && $curRes->is_problem()) { my $parts = $curRes->parts(); my $title = $curRes->compTitle(); my $symbx = $curRes->symb(); @@ -4194,9 +4258,8 @@ sub updateGradeByPage { $displayPts[1].=' Part: '.$display_part.' = '. (($score eq 'excused') ? 'excused' : $newpts). ' 
'; - $question++; - next if ($dropMenu eq 'reset status' || ($newpts == $oldpts && $score ne 'excused')); + next if ($dropMenu eq 'reset status' || ($newpts eq $oldpts && $score ne 'excused')); $newrecord{'resource.'.$partid.'.awarded'} = $partial if $partial ne ''; $newrecord{'resource.'.$partid.'.solved'} = $score if $score ne ''; @@ -4206,9 +4269,24 @@ sub updateGradeByPage { $changeflag++; } if (scalar(keys(%newrecord)) > 0) { + my %record = + &Apache::lonnet::restore($symbx,$env{'request.course.id'}, + $udom,$uname); + + if (&Apache::lonnet::validCODE($env{'form.CODE'})) { + $newrecord{'resource.CODE'} = $env{'form.CODE'}; + } elsif (&Apache::lonnet::validCODE($record{'resource.CODE'})) { + $newrecord{'resource.CODE'} = ''; + } &Apache::lonnet::cstore(\%newrecord,$symbx,$env{'request.course.id'}, $udom,$uname); + %record = &Apache::lonnet::restore($symbx, + $env{'request.course.id'}, + $udom,$uname); + &check_and_remove_from_queue($parts,\%record,undef,$symbx, + $cdom,$cnum,$udom,$uname); } + if ($aggregateflag) { &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate, $env{'course.'.$env{'request.course.id'}.'.domain'}, @@ -4322,14 +4400,14 @@ sub scantron_CODElist { } sub scantron_CODEunique { - my $result=' + my $result=' - - + + - '; + '; return $result; } @@ -4448,6 +4526,7 @@ SCANTRONFORM
+ $default_form_data @@ -4564,6 +4643,7 @@ sub scantron_fixup_scanline { $answer=$alphabet[$args->{'response'}]; } elsif ($on eq 'number') { $answer=$args->{'response'}+1; + if ($answer == 10) { $answer = '0'; } } else { substr($answer,$args->{'response'},1)=$on; } @@ -4634,7 +4714,7 @@ sub scantron_parse_scanline { || $currentquest eq '*') { push(@{$record{'scantron.doubleerror'}},$questnum); $record{"scantron.$questnum.answer"}=''; - } elsif (!$currentquest + } elsif (!defined($currentquest) || $currentquest eq $$scantron_config{'Qoff'} || $currentquest !~ /^[A-Z]$/) { $record{"scantron.$questnum.answer"}=''; @@ -4649,9 +4729,9 @@ sub scantron_parse_scanline { || $currentquest eq '*') { push(@{$record{'scantron.doubleerror'}},$questnum); $record{"scantron.$questnum.answer"}=''; - } elsif (!$currentquest - || $currentquest eq $$scantron_config{'Qoff'} - || $currentquest !~ /^\d$/) { + } elsif (!defined($currentquest) + || $currentquest eq $$scantron_config{'Qoff'} + || $currentquest !~ /^\d$/) { $record{"scantron.$questnum.answer"}=''; if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) { push(@{$record{"scantron.missingerror"}},$questnum); @@ -5639,8 +5719,9 @@ SCANTRONFORM 'grade_domain' =>$udom, 'grade_courseid'=>$env{'request.course.id'}, 'grade_symb' =>$resource->symb()); - if (exists($scan_record->{'scantron.CODE'}) && - $scan_record->{'scantron.CODE'}) { + if (exists($scan_record->{'scantron.CODE'}) + && + &Apache::lonnet::validCODE($scan_record->{'scantron.CODE'})) { $form{'CODE'}=$scan_record->{'scantron.CODE'}; } else { $form{'CODE'}='';