--- loncom/homework/grades.pm 2002/09/20 23:35:30 1.49 +++ loncom/homework/grades.pm 2002/10/04 06:22:12 1.54 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.49 2002/09/20 23:35:30 albertel Exp $ +# $Id: grades.pm,v 1.54 2002/10/04 06:22:12 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -51,7 +51,7 @@ sub getpartlist { my @parts =(); my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys')); foreach my $key (@metakeys) { - if ( $key =~ m/stores_([0-9]+)_.*/) { + if ( $key =~ m/stores_(\w+)_.*/) { push(@parts,$key); } } @@ -91,7 +91,7 @@ sub response_type { my %seen = (); my (@partlist,%handgrade); foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) { - if (/^\w+response_\d+.*/) { + if (/^\w+response_\w+.*/) { my ($responsetype,$part) = split(/_/,$_,2); my ($partid,$respid) = split(/_/,$part); $handgrade{$part} = $responsetype.':'.($allkeys =~ /parameter_$part\_handgrade/ ? 'yes' : 'no'); @@ -265,7 +265,7 @@ sub verifyreceipt { my ($string,$contents,$matches) = ('','',0); my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist('all','0'); - foreach (sort {$$fullname{$a} cmp $$fullname{$b} } keys %$fullname) { + 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)) { @@ -292,7 +292,7 @@ sub verifyreceipt { $contents. '</table></td></tr></table>'."\n"; } - return $string.&show_grading_menu_form ($symb,$url); + return $string.&show_grading_menu_form($symb,$url); } #--- This is called by a number of programs. @@ -401,7 +401,7 @@ LISTJAVASCRIPT $gradeTable.='</tr>'."\n"; my $ctr = 0; - foreach my $student (sort {$$fullname{$a} cmp $$fullname{$b} } keys %$fullname) { + foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { my ($uname,$udom) = split(/:/,$student); my (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist); my $statusflg = ''; @@ -436,14 +436,14 @@ LISTJAVASCRIPT $gradeTable.='</table></td></tr></table>'. '<input type="button" '. 'onClick="javascript:checkSelect(this.form.stuinfo);" '. - 'value="'.$viewgrade.'" /><form />'."\n"; + 'value="'.$viewgrade.'" /></form>'."\n"; if ($ctr == 0) { $gradeTable='<br /> <font color="red">'. 'No submission found for this resource.</font><br />'; - $gradeTable.=&show_grading_menu_form($symb,$url); } elsif ($ctr == 1) { $gradeTable =~ s/type=checkbox/type=checkbox checked/; } + $gradeTable.=&show_grading_menu_form($symb,$url); $request->print($gradeTable); return ''; } @@ -1145,6 +1145,7 @@ KEYWORDS $endform.='(Next and Previous do not save the scores.)'."\n" if ($ENV{'form.handgrade'} eq 'yes'); $endform.='</td><tr></table></form>'; + $endform.=&show_grading_menu_form($symb,$url); $request->print($endform); } return ''; @@ -1299,7 +1300,7 @@ sub processHandGrade { my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0'); my (@parsedlist,@nextlist); my ($nextflg) = 0; - foreach (sort {$$fullname{$a} cmp $$fullname{$b} } keys %$fullname) { + foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { if ($nextflg == 1 && $button =~ /Next$/) { push @parsedlist,$_; } @@ -1440,10 +1441,10 @@ sub viewgrades_js { for (i=0;i<document.classgrade.total.value;i++) { var user = eval("document.classgrade.ctr"+i+".value"); var scorename = eval("document.classgrade.GD_"+user+ - "_"+partid+"_aw"); + "_"+partid+"_awarded"); var saveval = eval("document.classgrade.GD_"+user+ - "_"+partid+"_sv_s.value"); - var selname = eval("document.classgrade.GD_"+user+"_"+partid+"_sv"); + "_"+partid+"_solved_s.value"); + var selname = eval("document.classgrade.GD_"+user+"_"+partid+"_solved"); if (saveval != "correct") { scorename.value = point; if (selname[0].selected != true) { @@ -1469,11 +1470,11 @@ sub viewgrades_js { for (i=0;i<document.classgrade.total.value;i++) { var user = eval("document.classgrade.ctr"+i+".value"); var scorename = eval("document.classgrade.GD_"+user+ - "_"+partid+"_aw"); + "_"+partid+"_awarded"); var saveval = eval("document.classgrade.GD_"+user+ - "_"+partid+"_sv_s.value"); + "_"+partid+"_solved_s.value"); var selname = eval("document.classgrade.GD_"+user+ - "_"+partid+"_sv"); + "_"+partid+"_solved"); if (saveval != "correct") { scorename.value = ""; selname[1].selected = true; @@ -1483,14 +1484,14 @@ sub viewgrades_js { for (i=0;i<document.classgrade.total.value;i++) { var user = eval("document.classgrade.ctr"+i+".value"); var scorename = eval("document.classgrade.GD_"+user+ - "_"+partid+"_aw"); + "_"+partid+"_awarded"); var saveval = eval("document.classgrade.GD_"+user+ - "_"+partid+"_sv_s.value"); + "_"+partid+"_solved_s.value"); var selname = eval("document.classgrade.GD_"+user+ - "_"+partid+"_sv"); + "_"+partid+"_solved"); if (saveval != "correct") { scorename.value = eval("document.classgrade.GD_"+user+ - "_"+partid+"_aw_s.value");; + "_"+partid+"_awarded_s.value");; selname[0].selected = true; } } @@ -1498,8 +1499,8 @@ sub viewgrades_js { } function changeSelect(partid,user) { - var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_sv"); - var textbox = eval("document.classgrade.GD_"+user+'_'+partid+"_aw"); + var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_solved"); + var textbox = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded"); var point = textbox.value; var weight = eval("document.classgrade.weight_"+partid+".value"); @@ -1520,9 +1521,9 @@ sub viewgrades_js { } function changeOneScore(partid,user) { - var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_sv"); + var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_solved"); if (selval[1].selected) { - var boxval = eval("document.classgrade.GD_"+user+'_'+partid+"_aw"); + var boxval = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded"); boxval.value = ""; } } @@ -1543,14 +1544,14 @@ sub viewgrades_js { for (i=0;i<document.classgrade.total.value;i++) { var user = eval("document.classgrade.ctr"+i+".value"); var resetscore = eval("document.classgrade.GD_"+user+ - "_"+partid+"_aw"); + "_"+partid+"_awarded"); resetscore.value = eval("document.classgrade.GD_"+user+ - "_"+partid+"_aw_s.value"); + "_"+partid+"_awarded_s.value"); var saveselval = eval("document.classgrade.GD_"+user+ - "_"+partid+"_sv_s.value"); + "_"+partid+"_solved_s.value"); - var selname = eval("document.classgrade.GD_"+user+"_"+partid+"_sv"); + var selname = eval("document.classgrade.GD_"+user+"_"+partid+"_solved"); if (saveselval == "excused") { if (selname[1].selected == false) { selname[1].selected = true;} } else { @@ -1583,12 +1584,22 @@ sub viewgrades { '<input type="hidden" name="url" value="'.$url.'" />'."\n". '<input type="hidden" name="command" value="editgrades" />'."\n". '<input type="hidden" name="section" value="'.$ENV{'form.section'}.'" />'."\n"; - $result.='To assign the same score for all the students use the radio buttons or '. - 'text box below. To assign scores individually fill in the score boxes for '. - 'each student in the table below. <font color="red">A part that has already '. - 'been graded does not get changed using the radio buttons or text box. '. - 'If needed, it has to be changed individually.</font>'; - + $result.='<h3>Assign Common Grade To '; + if ($ENV{'form.section'} eq 'all') { + $result.='Class </h3>'; + } elsif ($ENV{'form.section'} eq 'no') { + $result.='Students in no Section </h3>'; + } else { + $result.='Students in Section '.$ENV{'form.section'}.'</h3>'; + } + $result.= '<table border=0><tr><td bgcolor="#777777">'."\n". + '<table border=0><tr bgcolor="#ffffdd"><td>'; +# $result.='To assign the same score for all the students use the radio buttons or '. +# 'text box below. To assign scores individually fill in the score boxes for '. +# 'each student in the table below. <font color="red">A part that has already '. +# 'been graded does not get changed using the radio buttons or text box. '. +# 'If needed, it has to be changed individually.</font>'; +# $result.='</td></tr><tr><td>'; #radio buttons/text box for assigning points for a section or class. #handles different parts of a problem my ($partlist,$handgrade) = &response_type($ENV{'form.url'}); @@ -1597,7 +1608,7 @@ sub viewgrades { $result.='<table border="0">'; my %seen = (); for (sort keys(%$handgrade)) { - my ($partid,$respid) = split (/_/); + my ($partid,$respid) = split (/_/,$_,2); next if $seen{$partid}; $seen{$partid}++; my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_}); @@ -1613,24 +1624,25 @@ sub viewgrades { my $ctr = 0; while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across $result.= '<td><input type="radio" name="RADVAL_'.$partid.'" '. - 'onclick="javascript:writePoint('.$partid.','.$weight{$partid}. + 'onclick="javascript:writePoint(\''.$partid.'\','.$weight{$partid}. ','.$ctr.')" />'.$ctr."</td>\n"; $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : ''); $ctr++; } $result.='</tr></table>'; $result.= '</td><td><b> or </b><input type="text" name="TEXTVAL_'. - $partid.'" size="4" '. - 'onChange="javascript:writePoint('.$partid.','.$weight{$partid}. - ',\'textval\')" /> /'. + $partid.'" size="4" '.'onChange="javascript:writePoint(\''. + $partid.'\','.$weight{$partid}.',\'textval\')" /> /'. $weight{$partid}.' (problem weight)</td>'."\n"; $result.= '</td><td><select name="SELVAL_'.$partid.'"'. - 'onChange="javascript:writeRadText('.$partid.','.$weight{$partid}.')" /> '. + 'onChange="javascript:writeRadText(\''.$partid.'\','. + $weight{$partid}.')" /> '. '<option selected="on"> </option>'. '<option>excused</option></select></td></tr>'."\n"; $ctsparts++; } - $result.='</table><input type="hidden" name="totalparts" value="'.$ctsparts.'" />'; + $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n". + '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />'; $result.='<input type="button" value="Reset" '. 'onClick="javascript:resetEntry('.$ctsparts.');" TARGET=_self> '; $result.='<input type="button" value="Submit Changes" '. @@ -1638,22 +1650,28 @@ sub viewgrades { #table listing all the students in a section/class #header of table + $result.= '<h3>Assign Grade to Specific Students in '; + if ($ENV{'form.section'} eq 'all') { + $result.='the Class </h3>'; + } elsif ($ENV{'form.section'} eq 'no') { + $result.='no Section </h3>'; + } else { + $result.='Section '.$ENV{'form.section'}.'</h3>'; + } $result.= '<table border=0><tr><td bgcolor="#777777">'."\n". '<table border=0><tr bgcolor="#deffff">'. '<td><b>Fullname</b></td><td><b>Username</b></td><td><b>Domain</b></td>'."\n"; my (@parts) = sort(&getpartlist($url)); foreach my $part (@parts) { my $display=&Apache::lonnet::metadata($url,$part.'.display'); - next if ($display =~ /^Number of Attempts/); if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); } if ($display =~ /^Partial Credit Factor/) { - $_ = $display; - my ($partid) = /.*?(\d+).*/; - $result.='<td><b>Score Part '.$partid.'<br>(weight = '. + my ($partid) = &split_part_type($part); + $result.='<td><b>Score Part '.$partid.'<br />(weight = '. $weight{$partid}.')</b></td>'."\n"; next; } - $display =~ s/Problem Status/Grade Status<br>/; + $display =~ s|Problem Status|Grade Status<br />|; $result.='<td><b>'.$display.'</b></td>'."\n"; } $result.='</tr>'; @@ -1662,7 +1680,7 @@ sub viewgrades { #list all the students - with points and grade status my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0'); my $ctr = 0; - foreach (sort {$$fullname{$a} cmp $$fullname{$b} } keys %$fullname) { + foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { my ($uname,$udom) = split(/:/); $result.='<input type="hidden" name="ctr'.$ctr.'" value="'.$uname.'" />'."\n"; $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'}, @@ -1687,30 +1705,36 @@ sub viewstudentgrade { '\')"; TARGET=_self>'.$fullname.'</a>'. '</td><td>'.$uname.'</td><td align="middle">'.$udom.'</td>'."\n"; foreach my $part (@$parts) { - my ($temp,$part,$type)=split(/_/,$part); + my ($part,$type) = &split_part_type($part); my $score=$record{"resource.$part.$type"}; - next if $type eq 'tries'; if ($type eq 'awarded') { my $pts = $score eq '' ? '' : $score*$$weight{$part}; $result.='<input type="hidden" name="'. - 'GD_'.$uname.'_'.$part.'_aw_s" value="'.$pts.'" />'."\n"; + 'GD_'.$uname.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n"; $result.='<td align="middle"><input type="text" name="'. - 'GD_'.$uname.'_'.$part.'_aw" '. - 'onChange="javascript:changeSelect('.$part.',\''.$uname. + 'GD_'.$uname.'_'.$part.'_awarded" '. + 'onChange="javascript:changeSelect(\''.$part.'\',\''.$uname. '\')" value="'.$pts.'" size="4" /></td>'."\n"; } elsif ($type eq 'solved') { my ($status,$foo)=split(/_/,$score,2); $status = 'nothing' if ($status eq ''); - $result.='<input type="hidden" name="'. - 'GD_'.$uname.'_'.$part.'_sv_s" value="'.$status.'" />'."\n"; + $result.='<input type="hidden" name="'.'GD_'.$uname.'_'. + $part.'_solved_s" value="'.$status.'" />'."\n"; $result.='<td align="middle"><select name="'. - 'GD_'.$uname.'_'.$part.'_sv" '. - 'onChange="javascript:changeOneScore('.$part.',\''.$uname.'\')" >'."\n"; + 'GD_'.$uname.'_'.$part.'_solved" '. + 'onChange="javascript:changeOneScore(\''.$part.'\',\''.$uname.'\')" >'."\n"; my $optsel = '<option selected="on"> </option><option>excused</option>'."\n"; $optsel = '<option> </option><option selected="on">excused</option>'."\n" if ($status eq 'excused'); $result.=$optsel; $result.="</select></td>\n"; + } else { + $result.='<input type="hidden" name="'. + 'GD_'.$uname.'_'.$part.'_'.$type.'_s" value="'.$score.'" />'. + "\n"; + $result.='<td align="middle"><input type="text" name="'. + 'GD_'.$uname.'_'.$part.'_'.$type.'" '. + 'value="'.$score.'" size="4" /></td>'."\n"; } } $result.='</tr>'; @@ -1743,20 +1767,40 @@ sub editgrades { my (@partid); my %weight = (); + my %columns = (); my ($i,$ctr,$count,$rec_update) = (0,0,0,0); + + my (@parts) = sort(&getpartlist($url)); + my $header; while ($ctr < $ENV{'form.totalparts'}) { my $partid = $ENV{'form.partid_'.$ctr}; push @partid,$partid; $weight{$partid} = $ENV{'form.weight_'.$partid}; $ctr++; - $result .= '<td colspan = 2 align="center"><b>Part '.$partid. - '</b> (Weight = '.$weight{$partid}.')</td>'; } - $result .= '</tr><tr bgcolor="#deffff">'; - foreach (@partid) { - $result .= '<td align="center"> <b>Old Score</b> </td>'. + foreach my $partid (@partid) { + $header .= '<td align="center"> <b>Old Score</b> </td>'. '<td align="center"> <b>New Score</b> </td>'; + $columns{$partid}=2; + foreach my $stores (@parts) { + my ($part,$type) = &split_part_type($stores); + if ($part !~ m/^\Q$partid\E/) { next;} + if ($type eq 'awarded' || $type eq 'solved') { next; } + my $display=&Apache::lonnet::metadata($url,$stores.'.display'); + $display =~ s/\[Part: (\w)+\]//; + $header .= '<td align="center"> <b>Old</b> '.$display.' </td>'. + '<td align="center"> <b>New</b> '.$display.' </td>'; + $columns{$partid}+=2; + } + } + foreach my $partid (@partid) { + $result .= '<td colspan="'.$columns{$partid}. + '" align="center"><b>Part '.$partid. + '</b> (Weight = '.$weight{$partid}.')</td>'; + } + $result .= '</tr><tr bgcolor="#deffff">'; + $result .= $header; $result .= '</tr>'."\n"; for ($i=0; $i<$ENV{'form.total'}; $i++) { @@ -1764,38 +1808,54 @@ sub editgrades { my %newrecord; my $updateflag = 0; my @userdom = grep /^$user:/,keys %$classlist; - my ($foo,$udom) = split(/:/,$userdom[0]); + my (undef,$udom) = split(/:/,$userdom[0]); $result .= '<tr bgcolor="#ffffde"><td>'.$user.' </td><td>'. $$fullname{$userdom[0]}.' </td>'; - foreach (@partid) { - my $old_aw = $ENV{'form.GD_'.$user.'_'.$_.'_aw_s'}; - my $old_part = $old_aw eq '' ? '' : $old_aw/$weight{$_}; - my $old_score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_sv_s'}}; - - my $awarded = $ENV{'form.GD_'.$user.'_'.$_.'_aw'}; - my $partial = $awarded eq '' ? '' : $awarded/$weight{$_}; + my $old_aw = $ENV{'form.GD_'.$user.'_'.$_.'_awarded_s'}; + my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1); + my $old_part = $old_aw eq '' ? '' : $old_part_pcr; + my $old_score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}}; + + my $awarded = $ENV{'form.GD_'.$user.'_'.$_.'_awarded'}; + my $pcr = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1); + my $partial = $awarded eq '' ? '' : $pcr; my $score; if ($partial eq '') { - $score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_sv_s'}}; + $score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}}; } elsif ($partial > 0) { $score = 'correct_by_override'; } elsif ($partial == 0) { $score = 'incorrect_by_override'; } - $score = 'excused' if (($ENV{'form.GD_'.$user.'_'.$_.'_sv'} eq 'excused') && + $score = 'excused' if (($ENV{'form.GD_'.$user.'_'.$_.'_solved'} eq 'excused') && ($score ne 'excused')); $result .= '<td align="center">'.$old_aw.' </td>'. '<td align="center">'.$awarded. ($score eq 'excused' ? $score : '').' </td>'; - next if ($old_part eq $partial && $old_score eq $score); - - $updateflag = 1; - $newrecord{'resource.'.$_.'.awarded'} = $partial if $partial ne ''; - $newrecord{'resource.'.$_.'.solved'} = $score; - $rec_update++; + if (!($old_part eq $partial && $old_score eq $score)) { + $updateflag = 1; + $newrecord{'resource.'.$_.'.awarded'} = $partial if $partial ne ''; + $newrecord{'resource.'.$_.'.solved'} = $score; + $rec_update++; + } + + my $partid=$_; + foreach my $stores (@parts) { + my ($part,$type) = &split_part_type($stores); + if ($part !~ m/^\Q$partid\E/) { next;} + if ($type eq 'awarded' || $type eq 'solved') { next; } + my $old_aw = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type.'_s'}; + my $awarded = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type}; + if ($awarded ne '' && $awarded ne $old_aw) { + $newrecord{'resource.'.$part.'.'.$type}= $awarded; + $updateflag=1; + } + $result .= '<td align="center">'.$old_aw.' </td>'. + '<td align="center">'.$awarded.' </td>'; + } } $result .= '</tr>'."\n"; if ($updateflag) { @@ -1811,6 +1871,15 @@ sub editgrades { '<b>Total number of students = '.$ENV{'form.total'}.'</b><br />'; return $title.$msg.$result; } + +sub split_part_type { + my ($partstr) = @_; + my ($temp,@allparts)=split(/_/,$partstr); + my $type=pop(@allparts); + my $part=join('.',@allparts); + return ($part,$type); +} + #------------- end of section for handling grading by section/class --------- # #---------------------------------------------------------------------------- @@ -2115,7 +2184,7 @@ sub view_edit_entire_class_form { my ($classlist,$sections) = &getclasslist('all','0'); my $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n"; $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n"; - $result.=' <b>View/Grade Entire Section/Class</b></td></tr>'."\n"; + $result.=' <b>Grade Entire Section or Class</b></td></tr>'."\n"; $result.='<tr bgcolor=#ffffe6><td>'."\n"; $result.='<form action="/adm/grades" method="post">'."\n". '<input type="hidden" name="symb" value="'.$symb.'" />'."\n". @@ -2128,7 +2197,7 @@ sub view_edit_entire_class_form { } } $result.='<option selected="on">all</select>'."<br />\n"; - $result.=' <input type="button" onClick="submit();" value="View/Grade" /></form>'."\n"; + $result.=' <input type="button" onClick="submit();" value="Grade" /></form>'."\n"; $result.='</td></tr></table>'."\n"; $result.='</td></tr></table>'."\n"; return $result;