--- loncom/homework/grades.pm 2002/07/25 21:25:38 1.41 +++ loncom/homework/grades.pm 2002/07/26 20:28:42 1.42 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.41 2002/07/25 21:25:38 ng Exp $ +# $Id: grades.pm,v 1.42 2002/07/26 20:28:42 ng Exp $ # # Copyright Michigan State University Board of Trustees # @@ -321,55 +321,6 @@ sub getpartlist { return @parts; } -#FIXME need to look at the metadata <stores> spec on what type of data to accept and provide an -#interface based on that, also do that to above function. -sub setstudentgrade { - my ($url,$symb,$courseid,$student,@parts) = @_; - my $result =''; - my ($stuname,$domain) = split(/:/,$student); - my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$stuname); - my %newrecord; - - foreach my $part (@parts) { - my ($temp,$part,$type)=split(/_/,$part); - my $oldscore=$record{"resource.$part.$type"}; - my $newscore=$ENV{"form.GRADE.$student.$part.$type"}; - if ($type eq 'solved') { - my $update=0; - if ($newscore eq 'nothing' ) { - if ($oldscore ne '') { - $update=1; - $newscore = ''; - } - } elsif ($oldscore !~ m/^$newscore/) { - $update=1; - $result.="Updating $stuname to $newscore<br />\n"; - if ($newscore eq 'correct') { $newscore = 'correct_by_override'; } - if ($newscore eq 'incorrect') { $newscore = 'incorrect_by_override'; } - if ($newscore eq 'excused') { $newscore = 'excused'; } - if ($newscore eq 'ungraded') { $newscore = 'ungraded_attempted'; } - } else { - #$result.="$stuname:$part:$type:unchanged $oldscore to $newscore:<br />\n"; - } - if ($update) { $newrecord{"resource.$part.$type"}=$newscore; } - } else { - if ($oldscore ne $newscore) { - $newrecord{"resource.$part.$type"}=$newscore; - $result.="Updating $student"."'s status for $part.$type to $newscore<br />\n"; - } else { - #$result.="$stuname:$part:$type:unchanged $oldscore to $newscore:<br />\n"; - } - } - } - if ( scalar(keys(%newrecord)) > 0 ) { - $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}"; -# &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$stuname); - - $result.="Stored away ".scalar(keys(%newrecord))." elements.<br />\n"; - } - return $result; -} - sub print_hash { my ($request, $hash) = @_; $request->print('<table border=1><tr><td>Key</td><td>Value</td></tr>'); @@ -1067,8 +1018,9 @@ sub verifyReceipt_form { return $result; } -sub viewgrades { - my ($request) = @_; +sub viewgrades_js { + my ($request) = shift; + $request->print(<<VIEWJAVASCRIPT); <script type="text/javascript" language="javascript"> function viewOneStudent(user) { @@ -1076,38 +1028,140 @@ sub viewgrades { document.onestudent.submit(); } - function writePoint(partid,weight,point) { + var radioButton = eval("document.classgrade.RADVAL_"+partid); + var textbox = eval("document.classgrade.TEXTVAL_"+partid); + if (point == "textval") { + var point = eval("document.classgrade.TEXTVAL_"+partid+".value"); + if (isNaN(point) || point < 0) { + alert("A number equal or greater than 0 is expected. Entered value = "+point); + var resetbox = false; + for (var i=0; i<radioButton.length; i++) { + if (radioButton[i].checked) { + textbox.value = i; + resetbox = true; + } + } + if (!resetbox) { + textbox.value = ""; + } + return; + } + for (var i=0; i<radioButton.length; i++) { + radioButton[i].checked=false; + if (point == i) { + radioButton[i].checked=true; + } + } + + } else { + textbox.value = point; + } for (i=0;i<document.classgrade.total.value;i++) { var user = eval("document.classgrade.counter"+i+".value"); - var scorename = eval("document.classgrade.GRADE_"+user+"_"+partid+"_awarded"); - scorename.value = point; + var scorename = eval("document.classgrade.GRADE_"+user+ + "_"+partid+"_awarded"); + var saveval = eval("document.classgrade.GRADE_"+user+ + "_"+partid+"_solved_save.value"); + var selname = eval("document.classgrade.GRADE_"+user+"_"+partid+"_solved"); + if (saveval != "correct") { + scorename.value = point; + selname[0].selected = true; + } } -} + var selval = eval("document.classgrade.SELVAL_"+partid); + selval[0].selected = true; + } + + function writeRadText(partid,weight) { + var selval = eval("document.classgrade.SELVAL_"+partid); + if (selval[1].selected) { + var radioButton = eval("document.classgrade.RADVAL_"+partid); + for (var i=0; i<radioButton.length; i++) { + radioButton[i].checked=false; + + } + var textbox = eval("document.classgrade.TEXTVAL_"+partid); + textbox.value = ""; + + for (i=0;i<document.classgrade.total.value;i++) { + var user = eval("document.classgrade.counter"+i+".value"); + var scorename = eval("document.classgrade.GRADE_"+user+ + "_"+partid+"_awarded"); + var saveval = eval("document.classgrade.GRADE_"+user+ + "_"+partid+"_solved_save.value"); + var selname = eval("document.classgrade.GRADE_"+user+ + "_"+partid+"_solved"); + if (saveval != "correct") { + scorename.value = ""; + selname[1].selected = true; + } + } + } + } + + function changeSelect(partid,user) { + var selval = eval("document.classgrade.GRADE_"+user+'_'+partid+"_solved"); + selval[0].selected = true; + } + + function changeOneScore(partid,user) { + var selval = eval("document.classgrade.GRADE_"+user+'_'+partid+"_solved"); + if (selval[1].selected) { + var boxval = eval("document.classgrade.GRADE_"+user+'_'+partid+"_awarded"); + boxval.value = ""; + } + } + + function resetEntry(numpart) { + for (ctpart=0;ctpart<numpart;ctpart++) { + var partid = eval("document.classgrade.partid_"+ctpart+".value"); + var radioButton = eval("document.classgrade.RADVAL_"+partid); + var textbox = eval("document.classgrade.TEXTVAL_"+partid); + var selval = eval("document.classgrade.SELVAL_"+partid); + for (var i=0; i<radioButton.length; i++) { + radioButton[i].checked=false; + + } + textbox.value = ""; + selval[0].selected = true; + + for (i=0;i<document.classgrade.total.value;i++) { + var user = eval("document.classgrade.counter"+i+".value"); + var resetscore = eval("document.classgrade.GRADE_"+user+ + "_"+partid+"_awarded"); + resetscore.value = eval("document.classgrade.GRADE_"+user+ + "_"+partid+"_awarded_save.value"); + + var saveselval = eval("document.classgrade.GRADE_"+user+ + "_"+partid+"_solved_save.value"); + + var selname = eval("document.classgrade.GRADE_"+user+"_"+partid+"_solved"); + if (saveselval == "excused") { + selname[1].selected = true; + } else { + selname[0].selected = true; + } + } + } + } + + </script> VIEWJAVASCRIPT +} + +sub viewgrades { + my ($request) = shift; + &viewgrades_js($request); my ($symb,$url) = ($ENV{'form.symb'},$ENV{'form.url'}); $request->print ('<h2><font color="#339933">Manual Grading</font></h2>'); - my $result='<table border="0">'; - $result.='<tr><td colspan=3><font size=+1><b>Resource: </b>'.$ENV{'form.url'}. - '</font></td></tr>'."\n"; - my ($partlist,$handgrade) = &response_type($ENV{'form.url'}); - my %weight = (); - for (sort keys(%$handgrade)) { - my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_}); - my ($partid,$respid) = split (/_/); - my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb); - $weight{$partid} = $wgt eq '' ? '1' : $wgt; - $result.='<tr><td><b>Part id: </b>'.$partid.'</td>'. - '<td><b>Type: </b>'.$responsetype.'</td>'. - '<td><b>Handgrade: </b>'.$handgrade.'</font></td></tr>'."\n"; - } - $request->print($result.'</table>'."\n"); + my $result='<font size=+1><b>Resource: </b>'.$ENV{'form.url'}.'</font>'."\n"; #view individual student submission form - called using Javascript viewOneStudent - $result = '<form action="/adm/grades" method="post" name="onestudent">'."\n". + $result.= '<form action="/adm/grades" method="post" name="onestudent">'."\n". '<input type="hidden" name="symb" value="'.$symb.'" />'."\n". '<input type="hidden" name="url" value="'.$url.'" />'."\n". '<input type="hidden" name="command" value="submission" />'."\n". @@ -1120,26 +1174,50 @@ VIEWJAVASCRIPT '<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 box below. '. - 'To assign individual score fill in the score for each student in the table below.<br />'; + + $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 score 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>'; + + my ($partlist,$handgrade) = &response_type($ENV{'form.url'}); + my %weight = (); + my $ctsparts = 0; $result.='<table border="0">'; - for (sort keys (%weight)) { + for (sort keys(%$handgrade)) { + my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_}); + my ($partid,$respid) = split (/_/); + my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb); + $weight{$partid} = $wgt eq '' ? '1' : $wgt; + + $result.='<input type="hidden" name="partid_'.$ctsparts.'" value="'.$partid.'" />'."\n"; + $result.='<tr><td><b>Part ID:</b> '.$partid.' </td><td>'; + $result.='<table border="0"><tr>'; my $ctr = 0; - $result.='<tr><td><b>Part</b> '.$_.'</td><td>'; - $result.='<table border="0"><tr>'; # display radio buttons in a nice table 10 across - while ($ctr<=$weight{$_}) { - $result.= '<td><input type="radio" name="RADVAL_'.$_.'" '. - 'onclick="javascript:writePoint('.$_.','.$weight{$_}. + 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}. ','.$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\')" /> /'. + $weight{$partid}.' (problem weight)</td>'."\n"; + $result.= '</td><td><select name="SELVAL_'.$partid.'"'. + 'onChange="javascript:writeRadText('.$partid.','.$weight{$partid}.')" /> '. + '<option selected="on"> </option>'. + '<option>excused</option></select></td></tr>'."\n"; + $ctsparts++; } - $result.='</tr></table>'; + $result.='</table><input type="hidden" name="totalparts" value="'.$ctsparts.'" />'; + $result.='<input type="button" value="Reset" '. + 'onClick="javascript:resetEntry('.$ctsparts.');" TARGET=_self>   '; + $result.= '<input type="submit" name="submit" value="Submit Changes" />'."\n"; - $result.= '<input type="submit" name="submit" value="Submit Changes" />'."\n". - '<table border=0><tr><td bgcolor="#777777">'."\n". + $result.= '<table border=0><tr><td bgcolor="#777777">'."\n". '<table border=0><tr bgcolor="#deffff">'. '<td><b>Username</b></td><td><b>Fullname</b></td><td><b>Domain</b></td>'."\n"; #get list of parts for this problem @@ -1151,10 +1229,11 @@ VIEWJAVASCRIPT if ($display =~ /^Partial Credit Factor/) { $_ = $display; my ($partid) = /.*?(\d+).*/; - $result.='<td><b>Score Part '.$partid.'<br>(weight = '.$weight{$partid}.')</b></td>'."\n"; + $result.='<td><b>Score Part '.$partid.'<br>(weight = '. + $weight{$partid}.')</b></td>'."\n"; next; } - $display =~ s/Problem Status/Grade Status/; + $display =~ s/Problem Status/Grade Status<br>/; $result.='<td><b>'.$display.'</b></td>'."\n"; } $result.='</tr>'; @@ -1187,20 +1266,24 @@ sub viewstudentgrade { my $score=$record{"resource.$part.$type"}; next if $type eq 'tries'; if ($type eq 'awarded') { - my $pts = $score*$$weight{$part}; - $result.='<td align="middle"><input type="text" name="GRADE_'.$username.'_'.$part.'_'.$type. - '" value="'.$pts.'" size="4" /></td>'."\n"; -# $result.='<td align="middle"><input type="text" name="GRADE.'.$student.'.'.$part.'.'.$type. -# '" value="'.$pts.'" size="4" /></td>'."\n"; -# } elsif ($type eq 'tries') { -# $result.='<td align="middle">'.$score.' </td>'."\n"; + my $pts = $score eq '' ? '' : $score*$$weight{$part}; + $result.='<input type="hidden" name="'. + 'GRADE_'.$username.'_'.$part.'_awarded_save" value="'.$pts.'" />'."\n"; + $result.='<td align="middle"><input type="text" name="'. + 'GRADE_'.$username.'_'.$part.'_awarded"'. + 'onChange="javascript:changeSelect('.$part.',\''.$username.'\')" value="'. + $pts.'" size="4" /></td>'."\n"; } elsif ($type eq 'solved') { my ($status,$foo)=split(/_/,$score,2); - $result.="<td align=\"middle\"><select name=\"GRADE.$student.$part.$type\">\n"; - my $optsel = '<option>correct</option><option>incorrect</option><option>excused</option>'. - '<option>ungraded</option><option>nothing</option>'."\n"; $status = 'nothing' if ($status eq ''); - $optsel =~ s/<option>$status/<option selected="on">$status/; + $result.='<input type="hidden" name="'. + 'GRADE_'.$username.'_'.$part.'_solved_save" value="'.$status.'" />'."\n"; + $result.='<td align="middle"><select name="'. + 'GRADE_'.$username.'_'.$part.'_solved" '. + 'onChange="javascript:changeOneScore('.$part.',\''.$username.'\')" >'."\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"; } @@ -1233,6 +1316,57 @@ sub editgrades { return $result; } + +#FIXME need to look at the metadata <stores> spec on what type of data to accept and provide an +#interface based on that, also do that to above function. +sub setstudentgrade { + my ($url,$symb,$courseid,$student,@parts) = @_; + my $result =''; + my ($stuname,$domain) = split(/:/,$student); + my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$stuname); + my %newrecord; + + foreach my $part (@parts) { + my ($temp,$part,$type)=split(/_/,$part); + my $oldscore=$record{"resource.$part.$type"}; + my $newscore=$ENV{"form.GRADE.$student.$part.$type"}; + if ($type eq 'solved') { + my $update=0; + if ($newscore eq 'nothing' ) { + if ($oldscore ne '') { + $update=1; + $newscore = ''; + } + } elsif ($oldscore !~ m/^$newscore/) { + $update=1; + $result.="Updating $stuname to $newscore<br />\n"; + if ($newscore eq 'correct') { $newscore = 'correct_by_override'; } + if ($newscore eq 'incorrect') { $newscore = 'incorrect_by_override'; } + if ($newscore eq 'excused') { $newscore = 'excused'; } + if ($newscore eq 'ungraded') { $newscore = 'ungraded_attempted'; } + } else { + #$result.="$stuname:$part:$type:unchanged $oldscore to $newscore:<br />\n"; + } + if ($update) { $newrecord{"resource.$part.$type"}=$newscore; } + } else { + if ($oldscore ne $newscore) { + $newrecord{"resource.$part.$type"}=$newscore; + $result.="Updating $student"."'s status for $part.$type to $newscore<br />\n"; + } else { + #$result.="$stuname:$part:$type:unchanged $oldscore to $newscore:<br />\n"; + } + } + } + if ( scalar(keys(%newrecord)) > 0 ) { + $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}"; +# &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$stuname); + + $result.="Stored away ".scalar(keys(%newrecord))." elements.<br />\n"; + } + return $result; +} + + sub sub_page_js { my $request = shift; $request->print(<<SUBJAVASCRIPT);