--- loncom/homework/grades.pm 2003/11/07 08:56:52 1.148 +++ loncom/homework/grades.pm 2005/02/12 03:43:06 1.245 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.148 2003/11/07 08:56:52 albertel Exp $ +# $Id: grades.pm,v 1.245 2005/02/12 03:43:06 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,16 +25,6 @@ # # http://www.lon-capa.org/ # -# 2/9,2/13 Guy Albertelli -# 6/8 Gerd Kortemeyer -# 7/26 H.K. Ng -# 8/20 Gerd Kortemeyer -# Year 2002 -# June-August H.K. Ng -# Year 2003 -# February, March H.K. Ng -# July, H. K. Ng -# package Apache::grades; use strict; @@ -48,6 +38,7 @@ use Apache::lonhomework; use Apache::loncoursedata; use Apache::lonmsg qw(:user_normal_msg); use Apache::Constants qw(:common); +use Apache::lonlocal; use String::Similarity; my %oldessays=(); @@ -88,30 +79,16 @@ sub getpartlist { # --- Get the symbolic name of a problem and the url sub get_symb_and_url { - my ($request) = @_; + 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 '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; } - return ($symb,$url); -} - -# --- Retrieve the fullname for a user. Return lastname, first middle --- -# --- Generation is attached next to the lastname if it exists. --- -sub get_fullname { - my ($uname,$udom) = @_; - my %name=&Apache::lonnet::get('environment', ['lastname','generation', - 'firstname','middlename'], - $udom,$uname); - my $fullname; - my ($tmp) = keys(%name); - if ($tmp !~ /^(con_lost|error|no_such_host)/i) { - $fullname = &Apache::loncoursedata::ProcessFullName - (@name{qw/lastname generation firstname middlename/}); - } else { - &Apache::lonnet::logthis('grades.pm: no name data for '.$uname. - '@'.$udom.':'.$tmp); + if ($symb eq '') { + if (!$silent) { + $request->print("Unable to handle ambiguous references:$url:."); + return (); + } } - return $fullname; + return ($symb,$url); } #--- Format fullname, username:domain if different for display @@ -132,6 +109,10 @@ sub response_type { my ($url,$symb) = shift; $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url))) if ($symb eq ''); 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'))) { @@ -141,6 +122,9 @@ sub response_type { 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'); @@ -154,21 +138,49 @@ sub response_type { return \@partlist,\%handgrade,\%responseType; } +sub get_display_part { + my ($partID,$url,$symb)=@_; + if (!defined($symb) || $symb eq '') { + $symb=$ENV{'form.symb'}; + if ($symb eq '') { $symb=&Apache::lonnet::symbread($url) } + } + my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb); + if (defined($display) and $display ne '') { + $display.= " (id $partID)"; + } else { + $display=$partID; + } + return $display; +} #--- Show resource title #--- and parts and response type sub showResourceInfo { - my ($url,$probTitle) = @_; + my ($url,$probTitle,$checkboxes) = @_; + my $col=3; + if ($checkboxes) { $col=4; } my $result =''. - ''."\n"; + ''."\n"; my ($partlist,$handgrade,$responseType) = &response_type($url); my %resptype = (); my $hdgrade='no'; + my %partsseen; for my $part_resID (sort keys(%$handgrade)) { my $handgrade=$$handgrade{$part_resID}; my ($partID,$resID) = split(/_/,$part_resID); my $responsetype = $responseType->{$partID}->{$resID}; $hdgrade = $handgrade if ($handgrade eq 'yes'); - $result.=''; + if ($checkboxes) { + if (exists($partsseen{$partID})) { + $result.=""; + } else { + $result.=""; + } + $partsseen{$partID}=1; + } + my $display_part=&get_display_part($partID,$url); + $result.=''. ''; # ''; @@ -189,8 +201,7 @@ sub get_order { ('grade_courseid' => $ENV{'request.course.id'}), ('grade_username' => $uname)); - (my $debug,$subresult)=split(/_HASH_REF__/,$subresult,2); - Apache->request->print($debug); + (undef,$subresult)=split(/_HASH_REF__/,$subresult,2); my %analyze=&Apache::lonnet::str2hash($subresult); return ($analyze{"$partid.$respid.shown"}); } @@ -271,7 +282,8 @@ sub cleanRecord { $ENV{'form.kwstyle'} = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : ''; $ENV{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob. } - return '

'.&keywords_highlight($answer).'
'; + $answer =~ s-\n-
-g; + return '

'.&keywords_highlight($answer).'
'; } return $answer; } @@ -323,27 +335,36 @@ sub getclasslist { # my %sections; my %fullnames; - foreach (keys(%$classlist)) { - # the following undefs are for 'domain', and 'username' respectively. - my (undef,undef,$end,$start,$id,$section,$fullname,$status)= - @{$classlist->{$_}}; + foreach my $student (keys(%$classlist)) { + my $end = + $classlist->{$student}->[&Apache::loncoursedata::CL_END()]; + my $start = + $classlist->{$student}->[&Apache::loncoursedata::CL_START()]; + my $id = + $classlist->{$student}->[&Apache::loncoursedata::CL_ID()]; + my $section = + $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()]; + my $fullname = + $classlist->{$student}->[&Apache::loncoursedata::CL_FULLNAME()]; + my $status = + $classlist->{$student}->[&Apache::loncoursedata::CL_STATUS()]; # filter students according to status selected if ($filterlist && $ENV{'form.Status'} ne 'Any') { if ($ENV{'form.Status'} ne $status) { - delete ($classlist->{$_}); + delete ($classlist->{$student}); next; } } - $section = ($section ne '' ? $section : 'no'); + $section = ($section ne '' ? $section : 'none'); if (&canview($section)) { if ($getsec eq 'all' || $getsec eq $section) { $sections{$section}++; - $fullnames{$_}=$fullname; + $fullnames{$student}=$fullname; } else { - delete($classlist->{$_}); + delete($classlist->{$student}); } } else { - delete($classlist->{$_}); + delete($classlist->{$student}); } } my %seen = (); @@ -485,7 +506,7 @@ sub verifyreceipt { my $request = shift; my $courseid = $ENV{'request.course.id'}; - my $receipt = unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'. + my $receipt = &Apache::lonnet::recprefix($courseid).'-'. $ENV{'form.receipt'}; $receipt =~ s/[^\-\d]//g; my $url = $ENV{'form.url'}; @@ -500,18 +521,27 @@ sub verifyreceipt { my ($string,$contents,$matches) = ('','',0); my (undef,undef,$fullname) = &getclasslist('all','0'); - + + my $receiptparts=0; + if ($ENV{"course.$courseid.receiptalg"} eq 'receipt2') { $receiptparts=1; } + my $parts=['0']; + if ($receiptparts) { ($parts)=&response_type($url,$symb); } foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { my ($uname,$udom)=split(/\:/); - if ($receipt eq - &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) { - $contents.=''."\n". - ''. - ''."\n"; - - $matches++; + foreach my $part (@$parts) { + if ($receipt eq &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb,$part)) { + $contents.=''."\n". + ''. + ''; + if ($receiptparts) { + $contents.=''; + } + $contents.=''."\n"; + + $matches++; + } } } if ($matches == 0) { @@ -524,8 +554,11 @@ sub verifyreceipt { '
Current Resource: '.$probTitle.'
'.&mt('Current Resource').': '. + $probTitle.'
Part '.$partID.' '. + $result.='
 Part: '.$display_part.' '. $resID.'Type: '.$responsetype.'
Handgrade: '.$handgrade.'
 '."\n". - ''.$$fullname{$_}.'  '.$uname.'  '.$udom.' 
 '."\n". + ''.$$fullname{$_}.'  '.$uname.'  '.$udom.'  '.$part.' 
'."\n". ''."\n". ''."\n". - ''."\n". - $contents. + ''; + if ($receiptparts) { + $string.=''; + } + $string.=''."\n".$contents. '
 Fullname  Username  Domain 
 Domain  Problem Part 
'."\n"; } return $string.&show_grading_menu_form($symb,$url); @@ -551,8 +584,7 @@ sub listStudents { my $result='

 '.$viewgrade. ' Submissions for a Student or a Group of Students

'; - my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$ENV{'form.probTitle'}); - $result.=$table; + my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$ENV{'form.probTitle'},($ENV{'form.showgrading'} eq 'yes')); $request->print(< @@ -592,7 +624,8 @@ LISTJAVASCRIPT my $checkhdgrade = ($ENV{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked' : ''; my $checklastsub = $checkhdgrade eq '' ? 'checked' : ''; - my $gradeTable='
'."\n". + my $gradeTable=''. + "\n".$table. ' View Problem Text: no '."\n". ' one student '."\n". ' all students
'."\n". @@ -644,7 +677,9 @@ LISTJAVASCRIPT ''.&nameUserString('header').''; if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { foreach (sort(@$partlist)) { - $gradeTable.=' Part '.(split(/_/))[0].' Status '; + my $display_part=&get_display_part((split(/_/))[0],$url,$symb); + $gradeTable.=' Part: '.$display_part. + ' Status '; } } $loop++; @@ -659,20 +694,25 @@ LISTJAVASCRIPT if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist); my $submitted = 0; - my $graded = 1; + my $graded = 0; foreach (keys(%status)) { $submitted = 1 if ($status{$_} ne 'nothing'); - $graded = 0 if ($status{$_} =~ /^correct/); + $graded = 1 if ($status{$_} !~ /^correct/); + my ($foo,$partid,$foo1) = split(/\./,$_); if ($status{'resource.'.$partid.'.submitted_by'} ne '') { $submitted = 0; + my ($part)=split(/\./,$partid); $gradeTable.=''; } } - next if (!$submitted && ($submitonly eq 'yes' || $submitonly eq 'graded')); - next if (!$graded && $submitonly eq 'graded'); + next if (!$submitted && ($submitonly eq 'yes' || + $submitonly eq 'incorrect' || + $submitonly eq 'graded')); + next if (!$graded && ($submitonly eq 'graded' || + $submitonly eq 'incorrect')); } $ctr++; @@ -712,9 +752,12 @@ LISTJAVASCRIPT if ($num_students eq 0) { $gradeTable='
 There are no students currently enrolled.'; } else { + my $submissions='submissions'; + if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; } + if ($submitonly eq 'graded' ) { $submissions = 'ungraded submissions'; } $gradeTable='
 '. - 'No submissions found for this resource for any students. ('.$num_students. - ' checked for submissions)
'; + 'No '.$submissions.' found for this resource for any students. ('.$num_students. + ' students checked for '.$submissions.')
'; } } elsif ($ctr == 1) { $gradeTable =~ s/type=checkbox/type=checkbox checked/; @@ -729,8 +772,7 @@ LISTJAVASCRIPT sub processGroup { my ($request) = shift; my $ctr = 0; - my @stuchecked = (ref($ENV{'form.stuinfo'}) ? @{$ENV{'form.stuinfo'}} - : ($ENV{'form.stuinfo'})); + my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo'); my $total = scalar(@stuchecked)-1; foreach (@stuchecked) { @@ -930,6 +972,8 @@ sub sub_page_kw_js { my $request = shift; my $iconpath = $request->dir_config('lonIconsURL'); &commonJSfunctions($request); + my $docopen=&Apache::lonhtmlcommon::javascript_docopen(); + $docopen=~s/^document\.//; $request->print(< @@ -1039,10 +1083,10 @@ sub sub_page_kw_js { var ypos = (screen.height-height)/2-30; ypos = (ypos < 0) ? '0' : ypos; - pWin = window.open('', 'MessageCenter', '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='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height); pWin.focus(); pDoc = pWin.document; - pDoc.open('text/html','replace'); + pDoc.$docopen; pDoc.write(""); pDoc.write("Message Central"); @@ -1170,10 +1214,10 @@ sub sub_page_kw_js { var ypos = (screen.height-330)/2-30; ypos = (ypos < 0) ? '0' : ypos; - hwdWin = window.open('', 'KeywordHighlightCentral', 'toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx='+xpos+',screeny='+ypos); + hwdWin = window.open('', 'KeywordHighlightCentral', 'resizeable=yes,toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx='+xpos+',screeny='+ypos); hwdWin.focus(); var hDoc = hwdWin.document; - hDoc.open('text/html','replace'); + hDoc.$docopen; hDoc.write(""); hDoc.write("Highlight Central"); @@ -1244,16 +1288,17 @@ sub gradeBox { '' : $$record{'resource.'.$partid.'.awarded'}*$wgt); my $result=''."\n"; + my $display_part=&get_display_part($partid,undef,$symb); $result.='
'. - 'Part '.$partid.' Points: '."\n"; + 'Part: '.$display_part.' Points: '."\n"; my $ctr = 0; $result.=''."\n"; # display radio buttons in a nice table 10 across while ($ctr<=$wgt) { - $result.= '\n"; + ($score eq $ctr ? 'checked':'').' /> '.$ctr."\n"; $result.=(($ctr+1)%10 == 0 ? '' : ''); $ctr++; } @@ -1341,19 +1386,22 @@ sub submission { my ($uname,$udom) = ($ENV{'form.student'},$ENV{'form.userdom'}); $udom = ($udom eq '' ? $ENV{'user.domain'} : $udom); #has form.userdom changed for a student? my $usec = &Apache::lonnet::getsection($udom,$uname,$ENV{'request.course.id'}); - $ENV{'form.fullname'} = &get_fullname ($uname,$udom) if $ENV{'form.fullname'} eq ''; + $ENV{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $ENV{'form.fullname'} eq ''; my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url))); if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; } if (!&canview($usec)) { $request->print('Unable to view requested student.('. - $uname.$udom.$usec.$ENV{'request.course.id'}.')'); + $uname.'@'.$udom.' in section '.$usec.' in course id '. + $ENV{'request.course.id'}.')'); $request->print(&show_grading_menu_form($symb,$url)); return; } - $ENV{'form.lastSub'} = ($ENV{'form.lastSub'} eq '' ? 'datesub' : $ENV{'form.lastSub'}); + if (!$ENV{'form.lastSub'}) { $ENV{'form.lastSub'} = 'datesub'; } + if (!$ENV{'form.vProb'}) { $ENV{'form.vProb'} = 'yes'; } + if (!$ENV{'form.vAns'}) { $ENV{'form.vAns'} = 'yes'; } my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : ''); my $checkIcon = ''; @@ -1435,6 +1483,9 @@ sub submission { ''."\n". ''."\n". ''."\n"); + foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) { + $request->print(''."\n"); + } } my ($cts,$prnmsg) = (1,''); @@ -1533,8 +1584,10 @@ KEYWORDS $result.=$$fullname{$_}.'     '; } $result.='
'."\n"; + my ($part)=split(/\./,$_); $result.=''."\n"; + '" value="'.$part.':'.(join ':',@goodcollaborators).'" />'. + "\n"; } if (scalar(@badcollaborators) > 0) { $result.='
'.$ctr."
\n"; - if ($$timestamp eq '') { - $lastsubonly.='\n"; + if ($$timestamp eq '') { + $lastsubonly.='
'; @@ -1560,76 +1613,95 @@ KEYWORDS # (3) Last submission plus the parts info # (4) The whole record for this student if ($ENV{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) { - if ($ENV{'form.'.$uname.':'.$udom.':submitted_by'}) { - my $submitby=''. - 'Collaborative submission by: '. - ''. - $$fullname{$ENV{'form.'.$uname.':'.$udom.':submitted_by'}}.''; - $request->print($submitby); - } else { - my ($string,$timestamp)= &get_last_submission(\%record); - my $lastsubonly=''. - ($$timestamp eq '' ? '' : 'Date Submitted: '. - $$timestamp)."
'.$$string[0]; - } else { - for my $part (sort keys(%$handgrade)) { - my ($partid,$respid) = split(/_/,$part); - my $responsetype = $responseType->{$partid}->{$respid}; - if (!exists($record{'resource.'.$partid.'.'.$respid.'.submission'})) { - $lastsubonly.='
Part '. - $partid.' ( ID '.$respid. - ' )   '. - 'Nothing submitted - no attempts

'; - } else { - foreach (@$string) { - my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/; - if ($part eq ($partid.'_'.$respid)) { - my ($ressub,$subval) = split(/:/,$_,2); - # Similarity check - my $similar=''; - my $oname; - my $odom; - my $ocrsid; - my $oessay; - my $osim; - if($ENV{'form.checkPlag'}){ - ($oname,$odom,$ocrsid,$oessay,$osim)=&most_similar($uname,$udom,$subval); - if ($osim) { - $osim=int($osim*100.0); - $similar='

Essay is '.$osim. - '% similar to an essay by '.&Apache::loncommon::plainname($oname,$odom). - '

'. - &keywords_highlight($oessay).'

'; - } - } - my $order=&get_order($partid,$respid,$symb,$uname,$udom); - $lastsubonly.='
Part '. - $partid.' ( ID '.$respid. - ' )   '. - ($record{"resource.$partid.$respid.uploadedurl"}? - ' File uploaded by student '. - 'Like all files provided by users, '. - 'this file may contain virusses
':''). - 'Submitted Answer: '. - &cleanRecord($subval,$responsetype,$symb,$partid,$respid,\%record,$order). - '

'.$similar."\n" - if ($ENV{'form.lastSub'} eq 'lastonly' || - ($ENV{'form.lastSub'} eq 'hdgrade' && - $$handgrade{$part} =~ /:yes$/)); + my ($string,$timestamp)= &get_last_submission(\%record); + my $lastsubonly=''. + ($$timestamp eq '' ? '' : 'Date Submitted: '. + $$timestamp)."
'.$$string[0]; + } else { + my %seenparts; + for my $part (sort keys(%$handgrade)) { + my ($partid,$respid) = split(/_/,$part); + my $display_part=&get_display_part($partid,$url,$symb); + if ($ENV{"form.$uname:$udom:$partid:submitted_by"}) { + if (exists($seenparts{$partid})) { next; } + $seenparts{$partid}=1; + my $submitby='Part: '.$display_part. + ' Collaborative submission by: '. + ''. + $$fullname{$ENV{"form.$uname:$udom:$partid:submitted_by"}}.'
'; + $request->print($submitby); + next; + } + my $responsetype = $responseType->{$partid}->{$respid}; + if (!exists($record{"resource.$partid.$respid.submission"})) { + $lastsubonly.='
Part: '. + $display_part.' ( ID '.$respid. + ' )   '. + 'Nothing submitted - no attempts

'; + next; + } + foreach (@$string) { + my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/; + if ($part ne ($partid.'_'.$respid)) { next; } + my ($ressub,$subval) = split(/:/,$_,2); + # Similarity check + my $similar=''; + if($ENV{'form.checkPlag'}){ + my ($oname,$odom,$ocrsid,$oessay,$osim)= + &most_similar($uname,$udom,$subval); + if ($osim) { + $osim=int($osim*100.0); + $similar="

Essay". + " is $osim% similar to an essay by ". + &Apache::loncommon::plainname($oname,$odom). + '

'. + &keywords_highlight($oessay). + '

'; + } + } + my $order=&get_order($partid,$respid,$symb,$uname,$udom); + if ($ENV{'form.lastSub'} eq 'lastonly' || + ($ENV{'form.lastSub'} eq 'hdgrade' && + $$handgrade{$part} eq 'yes')) { + my $display_part=&get_display_part($partid,$url,$symb); + $lastsubonly.='
Part: '. + $display_part.' ( ID '.$respid. + ' )   '; + my @files; + if ($record{"resource.$partid.$respid.portfiles"}) { + my $file_url = '/uploaded/'.$udom.'/'.$uname.'/portfolio'; + foreach my $file (split(',',$record{"resource.$partid.$respid.portfiles"})) { + push(@files,$file_url.$file); + + &Apache::lonnet::logthis("found a portfolio file".$record{"resource.$partid.$respid.portfiles"}); + &Apache::lonnet::logthis("uploaded URL file".$record{"resource.$partid.$respid.uploadedurl"}); } } + if ($record{"resource.$partid.$respid.uploadedurl"}) { + push(@files,$record{"resource.$partid.$respid.uploadedurl"}); + } + if (@files) { + $lastsubonly.='
Like all files provided by users, this file may contain virusses
'; + foreach my $file (@files) { + &Apache::lonnet::allowuploaded('/adm/grades',$file); + $lastsubonly.='
'.$file.''; + } + $lastsubonly.='
'; + } + $lastsubonly.='Submitted Answer: '. + &cleanRecord($subval,$responsetype,$symb,$partid, + $respid,\%record,$order); + if ($similar) {$lastsubonly.="

$similar\n";} } } } - $lastsubonly.='
'."\n"; - $request->print($lastsubonly); } + $lastsubonly.='
'."\n"; + $request->print($lastsubonly); } elsif ($ENV{'form.lastSub'} eq 'datesub') { my (undef,$responseType,undef,$parts) = &showResourceInfo($url); $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom)); @@ -1648,12 +1720,15 @@ KEYWORDS my $toGrade.='  '."\n" if (&canmodify($usec)); - $toGrade.='
'."\n"; - $toGrade.=&show_grading_menu_form($symb,$url) - if (($ENV{'form.command'} eq 'submission') || - ($ENV{'form.command'} eq 'processGroup' && $counter == $total)); - $request = print($toGrade); + $toGrade.=''."\n"; + if (($ENV{'form.command'} eq 'submission') || + ($ENV{'form.command'} eq 'processGroup' && $counter == $total)) { + $toGrade.=''.&show_grading_menu_form($symb,$url) + } + $request->print($toGrade); return; + } else { + $request->print(''."\n"); } # essay grading message center @@ -1805,18 +1880,21 @@ sub processHandGrade { $ENV{'form.msgsub'},$message); } if ($ENV{'form.collaborator'.$ctr}) { - my (@collaborators) = split(/:/,$ENV{'form.collaborator'.$ctr}); - foreach (@collaborators) { - my ($errorflag,$pts,$wgt) = - &saveHandGrade($request,$url,$symb,$_,$udom,$ctr,$ENV{'form.unamedom'.$ctr}); - if ($errorflag eq 'not_allowed') { - $request->print("Not allowed to modify grades for $_:$udom"); - next; - } else { - if ($message ne '') { - $msgstatus = &Apache::lonmsg::user_normal_msg ($_,$udom, - $ENV{'form.msgsub'}, - $message); + my @collabstrs=&Apache::loncommon::get_env_multiple("form.collaborator$ctr"); + foreach my $collabstr (@collabstrs) { + my ($part,@collaborators) = split(/:/,$collabstr); + foreach (@collaborators) { + my ($errorflag,$pts,$wgt) = + &saveHandGrade($request,$url,$symb,$_,$udom,$ctr, + $ENV{'form.unamedom'.$ctr},$part); + if ($errorflag eq 'not_allowed') { + $request->print("Not allowed to modify grades for $_:$udom"); + next; + } else { + if ($message ne '') { + $msgstatus = &Apache::lonmsg::user_normal_msg($_,$udom,$ENV{'form.msgsub'},$message); + } + } } } @@ -1924,7 +2002,7 @@ sub processHandGrade { foreach my $student (@parsedlist) { my $submitonly=$ENV{'form.submitonly'}; my ($uname,$udom) = split(/:/,$student); - if ($submitonly =~ /^(yes|graded)$/) { + if ($submitonly =~ /^(yes|graded|incorrect)$/) { # my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname); my %status=&student_gradeStatus($url,$symb,$udom,$uname,$partlist); my $submitted = 0; @@ -1937,8 +2015,11 @@ sub processHandGrade { $submitted = 0; } } - next if (!$submitted && ($submitonly eq 'yes' || $submitonly eq 'graded')); - next if (!$graded && $submitonly eq 'graded'); + next if (!$submitted && ($submitonly eq 'yes' || + $submitonly eq 'incorrect' || + $submitonly eq 'graded')); + next if (!$graded && ($submitonly eq 'graded' || + $submitonly eq 'incorrect')); } push @nextlist,$student if ($ctr < $ntstu); last if ($ctr == $ntstu); @@ -1968,7 +2049,7 @@ sub processHandGrade { #---- Save the score and award for each student, if changed sub saveHandGrade { - my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter) = @_; + my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_; my $usec = &Apache::lonnet::getsection($domain,$stuname, $ENV{'request.course.id'}); if (!&canmodify($usec)) { return('not_allowed'); } @@ -1976,6 +2057,8 @@ sub saveHandGrade { my %newrecord = (); my ($pts,$wgt) = ('',''); foreach (split(/:/,$ENV{'form.partlist'.$newflg})) { + #collaborator may vary for different parts + if ($submitter && $_ ne $part) { next; } my $dropMenu = $ENV{'form.GD_SEL'.$newflg.'_'.$_}; if ($dropMenu eq 'excused') { if ($record{'resource.'.$_.'.solved'} ne 'excused') { @@ -1987,36 +2070,46 @@ sub saveHandGrade { } } elsif ($dropMenu eq 'reset status' && exists($record{'resource.'.$_.'.solved'})) { #don't bother if no old records -> no attempts - $newrecord{'resource.'.$_.'.tries'} = 0; - $newrecord{'resource.'.$_.'.solved'} = ''; - $newrecord{'resource.'.$_.'.award'} = ''; - $newrecord{'resource.'.$_.'.awarded'} = 0; - $newrecord{'resource.'.$_.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}"; + foreach my $key (keys (%record)) { + if ($key=~/^resource\.\Q$_\E\./) { $newrecord{$key} = ''; } + } + $newrecord{'resource.'.$_.'.regrader'}= + "$ENV{'user.name'}:$ENV{'user.domain'}"; } elsif ($dropMenu eq '') { $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ? $ENV{'form.GD_BOX'.$newflg.'_'.$_} : $ENV{'form.RADVAL'.$newflg.'_'.$_}); - return 'no_score' if ($pts eq '' && $ENV{'form.GD_SEL'.$newflg.'_'.$_} eq ''); + if ($pts eq '' && $ENV{'form.GD_SEL'.$newflg.'_'.$_} eq '') { + next; + } $wgt = $ENV{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 : $ENV{'form.WGT'.$newflg.'_'.$_}; my $partial= $pts/$wgt; - next if ($partial eq $record{'resource.'.$_.'.awarded'}); #do not update score for part if not changed. - $newrecord{'resource.'.$_.'.awarded'} = $partial - if ($record{'resource.'.$_.'.awarded'} ne $partial); + if ($partial eq $record{'resource.'.$_.'.awarded'}) { + #do not update score for part if not changed. + next; + } + if ($record{'resource.'.$_.'.awarded'} ne $partial) { + $newrecord{'resource.'.$_.'.awarded'} = $partial; + } my $reckey = 'resource.'.$_.'.solved'; if ($partial == 0) { - $newrecord{$reckey} = 'incorrect_by_override' - if ($record{$reckey} ne 'incorrect_by_override'); + if ($record{$reckey} ne 'incorrect_by_override') { + $newrecord{$reckey} = 'incorrect_by_override'; + } } else { - $newrecord{$reckey} = 'correct_by_override' - if ($record{$reckey} ne 'correct_by_override'); + if ($record{$reckey} ne 'correct_by_override') { + $newrecord{$reckey} = 'correct_by_override'; + } + } + if ($submitter && + ($record{'resource.'.$_.'.submitted_by'} ne $submitter)) { + $newrecord{'resource.'.$_.'.submitted_by'} = $submitter; } - $newrecord{'resource.'.$_.'.submitted_by'} = $submitter - if ($submitter && ($record{'resource.'.$_.'.submitted_by'} ne $submitter)); - $newrecord{'resource.'.$_.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}"; + $newrecord{'resource.'.$_.'.regrader'}= + "$ENV{'user.name'}:$ENV{'user.domain'}"; } } - if (scalar(keys(%newrecord)) > 0) { &Apache::lonnet::cstore(\%newrecord,$symb, $ENV{'request.course.id'},$domain,$stuname); @@ -2199,8 +2292,14 @@ sub viewgrades { &viewgrades_js($request); my ($symb,$url) = ($ENV{'form.symb'},$ENV{'form.url'}); - my $result='

Manual Grading

'; + #need to make sure we have the correct data for later EXT calls, + #thus invalidate the cache + &Apache::lonnet::devalidatecourseresdata( + $ENV{'course.'.$ENV{'request.course.id'}.'.num'}, + $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}); + &Apache::lonnet::clear_EXT_cache_status(); + my $result='

'.&mt('Manual Grading').'

'; $result.='Current Resource: '.$ENV{'form.probTitle'}.''."\n"; #view individual student submission form - called using Javascript viewOneStudent @@ -2219,7 +2318,7 @@ sub viewgrades { my $sectionClass; if ($ENV{'form.section'} eq 'all') { $sectionClass='Class '; - } elsif ($ENV{'form.section'} eq 'no') { + } elsif ($ENV{'form.section'} eq 'none') { $sectionClass='Students in no Section '; } else { $sectionClass='Students in Section '.$ENV{'form.section'}.''; @@ -2246,7 +2345,8 @@ sub viewgrades { $ctsparts.'" value="'.$partid.'" />'."\n"; $result.=''."\n"; - $result.='Part '.$partid.'   Point: '; + my $display_part=&get_display_part($partid,$url,$symb); + $result.='Part: '.$display_part.'   Point: '; $result.=''; my $ctr = 0; while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across @@ -2285,14 +2385,17 @@ sub viewgrades { my $display=&Apache::lonnet::metadata($url,$part.'.display'); $display =~ s|^Number of Attempts|Tries
|; # makes the column narrower if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); } + my ($partid) = &split_part_type($part); + my $display_part=&get_display_part($partid,$url,$symb); if ($display =~ /^Partial Credit Factor/) { - my ($partid) = &split_part_type($part); - $result.=''."\n"; + $result.=''."\n"; next; + } else { + $display =~s/\[Part: \Q$partid\E\]/Part:<\/b> $display_part/; } $display =~ s|Problem Status|Grade Status
|; - $result.=''."\n"; + $result.=''."\n"; } $result.=''; @@ -2301,9 +2404,6 @@ sub viewgrades { my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'1'); my $ctr = 0; foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { - my $uname = $_; - $uname=~s/:/_/; - $result.=''."\n"; $ctr++; $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'}, $_,$$fullname{$_},\@parts,\%weight,$ctr); @@ -2327,18 +2427,21 @@ sub viewstudentgrade { my ($uname,$udom) = split(/:/,$student); $student=~s/:/_/; my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname); - my $result=''."\n"; foreach my $apart (@$parts) { my ($part,$type) = &split_part_type($apart); my $score=$record{"resource.$part.$type"}; + $result.=''."\n"; @@ -2347,7 +2450,7 @@ sub viewstudentgrade { $status = 'nothing' if ($status eq ''); $result.=''."\n"; - $result.=''."\n"; } @@ -2422,9 +2525,10 @@ sub editgrades { } } foreach my $partid (@partid) { + my $display_part=&get_display_part($partid,$url,$symb); $result .= ''; + '" align="center">Part: '.$display_part. + ' (Weight = '.$weight{$partid}.')'; } $result .= ''; @@ -2518,7 +2622,7 @@ sub editgrades { if ($noupdate) { # my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3; my $numcols=scalar(@partid)*4+2; - $result .= ''.$noupdate; + $result .= ''.$noupdate; } $result .= '
Score Part '.$partid.'
(weight = '. - $weight{$partid}.')
Score Part: '.$display_part. + '
(weight = '.$weight{$partid}.')
'.$display.''.$display.'
'.$ctr.'  '. + my $result='
'. + ''. + "\n".$ctr.'  '. ''.$fullname.' '. '('.$uname.($ENV{'user.domain'} eq $udom ? '' : ':'.$udom).')'; if ($type eq 'awarded') { my $pts = $score eq '' ? '' : $score*$$weight{$part}; $result.=''."\n"; - $result.=' '. "\n"; - $result.='Part '.$partid. - ' (Weight = '.$weight{$partid}.')
No Changes Occurred For the Students Below
No Changes Occurred For the Students Below
'."\n". &show_grading_menu_form ($symb,$url); @@ -2551,15 +2655,17 @@ sub csvupload_javascript_reverse_associa function verify(vf) { var foundsomething=0; var founduname=0; + var foundID=0; var founddomain=0; for (i=0;i<=vf.nfields.value;i++) { tw=eval('vf.f'+i+'.selectedIndex'); - if (i==0 && tw!=0) { founduname=1; } - if (i==1 && tw!=0) { founddomain=1; } - if (i!=0 && i!=1 && tw!=0) { foundsomething=1; } + if (i==0 && tw!=0) { foundID=1; } + if (i==1 && tw!=0) { founduname=1; } + if (i==2 && tw!=0) { founddomain=1; } + if (i!=0 && i!=1 && i!=2 && tw!=0) { foundsomething=1; } } - if (founduname==0 || founddomain==0) { - alert('You need to specify at both the username and domain'); + if ((founduname==0 && foundID==0) || founddomain==0) { + alert('You need to specify the domain and either the username or ID'); return; } if (foundsomething==0) { @@ -2589,15 +2695,17 @@ sub csvupload_javascript_forward_associa function verify(vf) { var foundsomething=0; var founduname=0; + var foundID=0; var founddomain=0; for (i=0;i<=vf.nfields.value;i++) { tw=eval('vf.f'+i+'.selectedIndex'); - if (tw==1) { founduname=1; } - if (tw==2) { founddomain=1; } - if (tw>2) { foundsomething=1; } + if (tw==1) { foundID=1; } + if (tw==2) { founduname=1; } + if (tw==3) { founddomain=1; } + if (tw>3) { foundsomething=1; } } - if (founduname==0 || founddomain==0) { - alert('You need to specify at both the username and domain'); + if ((founduname==0 && foundID==0) || founddomain==0) { + alert('You need to specify the domain and either the username or ID'); return; } if (foundsomething==0) { @@ -2629,7 +2737,8 @@ sub csvuploadmap_header { } my ($result) = &showResourceInfo($url,$ENV{'form.probTitle'}); - + my $checked=(($ENV{'form.noFirstLine'})?' checked="checked"':''); + my $ignore=&mt('Ignore First Line'); $request->print(<

Uploading Class Grades

@@ -2640,6 +2749,7 @@ Total number of records found in file: $ Enter as many fields as you can. The system will inform you and bring you back to this page if the data selected is insufficient to run your class.
+ @@ -2664,13 +2774,18 @@ ENDPICK sub csvupload_fields { my ($url,$symb) = @_; my (@parts) = &getpartlist($url,$symb); - my @fields=(['username','Student Username'],['domain','Student Domain']); + my @fields=(['ID','Student ID'], + ['username','Student Username'], + ['domain','Student Domain']); foreach my $part (sort(@parts)) { my @datum; my $display=&Apache::lonnet::metadata($url,$part.'.display'); my $name=$part; if (!$display) { $display = $name; } @datum=($name,$display); + if ($name=~/^stores_(.*)_awarded/) { + push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]); + } push(@fields,\@datum); } return (@fields); @@ -2711,6 +2826,7 @@ CSVFORMJS '.'."\n"; $result.=''."\n"; my $upfile_select=&Apache::loncommon::upfile_select_html(); + my $ignore=&mt('Ignore First Line'); $result.=< @@ -2720,7 +2836,7 @@ CSVFORMJS $upfile_select
- +