--- loncom/homework/grades.pm	2005/05/15 01:11:32	1.265
+++ loncom/homework/grades.pm	2005/06/04 03:36:36	1.271
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.265 2005/05/15 01:11:32 www Exp $
+# $Id: grades.pm,v 1.271 2005/06/04 03:36:36 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -152,6 +152,7 @@ sub get_display_part {
     }
     return $display;
 }
+
 #--- Show resource title
 #--- and parts and response type
 sub showResourceInfo {
@@ -284,6 +285,11 @@ sub cleanRecord {
 	}
 	$answer =~ s-\n-<br />-g;
 	return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>';
+    } elsif ( $response eq 'organic') {
+	my $result='Smile representation: "<tt>'.$answer.'</tt>"';
+	my $jme=$record->{$version."resource.$partid.$respid.molecule"};
+	$result.=&Apache::chemresponse::jme_img($jme,$answer,400);
+	return $result;
     }
     return $answer;
 }
@@ -626,24 +632,24 @@ LISTJAVASCRIPT
     my $checklastsub = $checkhdgrade eq '' ? 'checked' : '';
     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.
 	"\n".$table.
-	'&nbsp;<b>View Problem Text: </b><input type="radio" name="vProb" value="no" checked="on" /> no '."\n".
-	'<input type="radio" name="vProb" value="yes" /> one student '."\n".
-	'<input type="radio" name="vProb" value="all" /> all students <br />'."\n".
-	'&nbsp;<b>View Answer: </b><input type="radio" name="vAns" value="no"  /> no '."\n".
-	'<input type="radio" name="vAns" value="yes" /> one student '."\n".
-	'<input type="radio" name="vAns" value="all" checked="on" /> all students <br />'."\n".
+	'&nbsp;<b>View Problem Text: </b><label><input type="radio" name="vProb" value="no" checked="on" /> no </label>'."\n".
+	'<label><input type="radio" name="vProb" value="yes" /> one student </label>'."\n".
+	'<label><input type="radio" name="vProb" value="all" /> all students </label><br />'."\n".
+	'&nbsp;<b>View Answer: </b><label><input type="radio" name="vAns" value="no"  /> no </label>'."\n".
+	'<label><input type="radio" name="vAns" value="yes" /> one student </label>'."\n".
+	'<label><input type="radio" name="vAns" value="all" checked="on" /> all students </label><br />'."\n".
 	'&nbsp;<b>Submissions: </b>'."\n";
     if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {
-	$gradeTable.='<input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> essay part only'."\n";
+	$gradeTable.='<label><input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> essay part only </label>'."\n";
     }
 
     my $saveStatus = $env{'form.Status'} eq '' ? 'Active' : $env{'form.Status'};
     $env{'form.Status'} = $saveStatus;
 
-    $gradeTable.='<input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last submission only'."\n".
-	'<input type="radio" name="lastSub" value="last" /> last submission & parts info'."\n".
-	'<input type="radio" name="lastSub" value="datesub" /> by dates and submissions'."\n".
-	'<input type="radio" name="lastSub" value="all" /> all details'."\n".
+    $gradeTable.='<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last submission only </label>'."\n".
+	'<label><input type="radio" name="lastSub" value="last" /> last submission & parts info </label>'."\n".
+	'<label><input type="radio" name="lastSub" value="datesub" /> by dates and submissions </label>'."\n".
+	'<label><input type="radio" name="lastSub" value="all" /> all details</label>'."\n".
 	'<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".
 	'<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".
 	'<input type="hidden" name="handgrade"   value="'.$env{'form.handgrade'}.'" /><br />'."\n".
@@ -671,7 +677,7 @@ LISTJAVASCRIPT
 	'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
 	'value="Next->" /> <br />'."\n";
     $gradeTable.=&check_buttons();
-    $gradeTable.='<input type="checkbox" name="checkPlag" checked="on">Check For Plagiarism</input>';
+    $gradeTable.='<label><input type="checkbox" name="checkPlag" checked="on" />Check For Plagiarism</label>';
     my ($classlist, undef, $fullname) = &getclasslist($getsec,'1');
     $gradeTable.='<table border="0"><tr><td bgcolor="#777777">'.
 	'<table border="0"><tr bgcolor="#e6ffff">';
@@ -1346,6 +1352,14 @@ sub gradeBox {
     my $result='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n";
 
     my $display_part=&get_display_part($partid,undef,$symb);
+
+    my %last_resets = &get_last_resets($symb,$env{'request.course.id'},
+				       [$partid]);
+    my $aggtries = $$record{'resource.'.$partid.'.tries'};
+    if ($last_resets{$partid}) {
+        $aggtries = &get_num_tries($record,$last_resets{$partid},$partid);
+    }
+
     $result.='<table border="0"><tr><td>'.
 	'<b>Part: </b>'.$display_part.' <b>Points: </b></td><td>'."\n";
 
@@ -1384,7 +1398,11 @@ sub gradeBox {
     $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".
 	'<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".
 	'<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.
-	$$record{'resource.'.$partid.'.solved'}.'" />'."\n";
+	$$record{'resource.'.$partid.'.solved'}.'" />'."\n".
+        '<input type="hidden" name="totaltries'.$counter.'_'.$partid.'" value="'.
+        $$record{'resource.'.$partid.'.tries'}.'" />'."\n".
+        '<input type="hidden" name="aggtries'.$counter.'_'.$partid.'" value="'.
+        $aggtries.'" />'."\n";
     $result.='</td></tr></table>'."\n";
     return $result;
 }
@@ -1593,7 +1611,6 @@ KEYWORDS
     }
 
     my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
-
     my ($partlist,$handgrade,$responseType) = &response_type($url,$symb);
 
     # Display student info
@@ -2117,6 +2134,8 @@ sub saveHandGrade {
     my @parts_graded;
     my %newrecord  = ();
     my ($pts,$wgt) = ('','');
+    my %aggregate = ();
+    my $aggregateflag = 0;
     foreach my $new_part (split(/:/,$env{'form.partlist'.$newflg})) {
 	#collaborator may vary for different parts
 	if ($submitter && $new_part ne $part) { next; }
@@ -2136,6 +2155,21 @@ sub saveHandGrade {
 	    }
 	    $newrecord{'resource.'.$new_part.'.regrader'}=
 		"$env{'user.name'}:$env{'user.domain'}";
+            my $totaltries = $record{'resource.'.$part.'.tries'};
+
+            my %last_resets = &get_last_resets($symb,$env{'request.course.id'},
+					       [$new_part]);
+            my $aggtries =$totaltries;
+            if ($last_resets{$new_part}) {
+                $aggtries = &get_num_tries(\%record,$last_resets{$new_part},
+					   $new_part);
+            }
+
+            my $solvedstatus = $record{'resource.'.$new_part.'.solved'};
+            if ($aggtries > 0) {
+                &decrement($symb,$new_part,\%aggregate,$aggtries,$totaltries,$solvedstatus);
+                $aggregateflag = 1;
+            }
 	} elsif ($dropMenu eq '') {
 	    $pts = ($env{'form.GD_BOX'.$newflg.'_'.$new_part} ne '' ? 
 		    $env{'form.GD_BOX'.$newflg.'_'.$new_part} : 
@@ -2184,9 +2218,73 @@ sub saveHandGrade {
 	&Apache::lonnet::cstore(\%newrecord,$symb,
 				$env{'request.course.id'},$domain,$stuname);
     }
+    if ($aggregateflag) {
+        &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
+                  $env{'course.'.$env{'request.course.id'}.'.domain'},
+                  $env{'course.'.$env{'request.course.id'}.'.num'});
+    }
     return '',$pts,$wgt;
 }
 
+# ----------- Provides number of tries since last reset.
+sub get_num_tries {
+    my ($record,$last_reset,$part) = @_;
+    my $timestamp = '';
+    my $num_tries = 0;
+    if ($$record{'version'}) {
+        for (my $version=$$record{'version'};$version>=1;$version--) {
+            if (exists($$record{$version.':resource.'.$part.'.solved'})) {
+                $timestamp = $$record{$version.':timestamp'};
+                if ($timestamp > $last_reset) {
+                    $num_tries ++;
+                } else {
+                    last;
+                }
+            }
+        }
+    }
+    return $num_tries;
+}
+
+# ----------- Determine decrements required in aggregate totals 
+sub decrement_aggs {
+    my ($symb,$part,$aggregate,$aggtries,$totaltries,$solvedstatus) = @_;
+    my %decrement = (
+                        attempts => 0,
+                        users => 0,
+                        correct => 0
+                    );
+    $decrement{'attempts'} = $aggtries;
+    if ($solvedstatus =~ /^correct/) {
+        $decrement{'correct'} = 1;
+    }
+    if ($aggtries == $totaltries) {
+        $decrement{'users'} = 1;
+    }
+    foreach my $type (keys (%decrement)) {
+        $$aggregate{$symb."\0".$part."\0".$type} = -$decrement{$type};
+    }
+    return;
+}
+
+# ----------- Determine timestamps for last reset of aggregate totals for parts  
+sub get_last_resets {
+    my ($symb,$courseid,$partids) =@_;
+    my %last_resets;
+    my $cdom = $env{'course.'.$courseid.'.domain'};
+    my $cname = $env{'course.'.$courseid.'.num'};
+    my @keys;
+    foreach my $part (@{$partids}) {
+	push(@keys,"$symb\0$part\0resettime");
+    }
+    my %results=&Apache::lonnet::get('nohist_resourcetracker',\@keys,
+				     $cdom,$cname);
+    foreach my $part (@{$partids}) {
+	$last_resets{$part}=$results{"$symb\0$part\0resettime"};
+    }
+    return %last_resets;
+}
+
 # ----------- Handles creating versions for portfolio files as answers
 sub version_portfiles {
     my ($record, $parts_graded, $courseid, $symb, $domain, $stuname, $v_flag) = @_;
@@ -2376,7 +2474,7 @@ sub viewgrades_js {
 	    if (selval[2].selected) {
 		document.classgrade["GD_"+user+'_'+partid+"_tries"].value = "0";
 	    }
-	}
+        }
     }
 
     function resetEntry(numpart) {
@@ -2494,7 +2592,7 @@ sub viewgrades {
 	    '<option selected="on"> </option>'.
 	    '<option>excused</option>'.
 	    '<option>reset status</option></select></td>'.
-            '<td><input type="checkbox" name="FORCE_'.$partid.'" /> Override "Correct"</td></tr>'."\n";
+            '<td><label><input type="checkbox" name="FORCE_'.$partid.'" /> Override "Correct"</label></td></tr>'."\n";
 	$ctsparts++;
     }
     $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n".
@@ -2509,11 +2607,13 @@ sub viewgrades {
 	'<table border=0><tr bgcolor="#deffff"><td>&nbsp;<b>No.</b>&nbsp;</td>'.
 	'<td>'.&nameUserString('header')."</td>\n";
     my (@parts) = sort(&getpartlist($url,$symb));
+    my @partids = ();
     foreach my $part (@parts) {
 	my $display=&Apache::lonnet::metadata($url,$part.'.display');
 	$display =~ s|^Number of Attempts|Tries<br />|; # makes the column narrower
 	if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
 	my ($partid) = &split_part_type($part);
+        push(@partids, $partid);
 	my $display_part=&get_display_part($partid,$url,$symb);
 	if ($display =~ /^Partial Credit Factor/) {
 	    $result.='<td><b>Score Part:</b> '.$display_part.
@@ -2527,6 +2627,9 @@ sub viewgrades {
     }
     $result.='</tr>';
 
+    my %last_resets = 
+	&get_last_resets($symb,$env{'request.course.id'},\@partids);
+
     #get info for each student
     #list all the students - with points and grade status
     my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');
@@ -2534,7 +2637,7 @@ sub viewgrades {
     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
 	$ctr++;
 	$result.=&viewstudentgrade($url,$symb,$env{'request.course.id'},
-				   $_,$$fullname{$_},\@parts,\%weight,$ctr);
+				   $_,$$fullname{$_},\@parts,\%weight,$ctr,\%last_resets);
     }
     $result.='</table></td></tr></table>';
     $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";
@@ -2551,10 +2654,11 @@ sub viewgrades {
 
 #--- call by previous routine to display each student
 sub viewstudentgrade {
-    my ($url,$symb,$courseid,$student,$fullname,$parts,$weight,$ctr) = @_;
+    my ($url,$symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_;
     my ($uname,$udom) = split(/:/,$student);
     $student=~s/:/_/;
     my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
+    my %aggregates = (); 
     my $result='<tr bgcolor="#ffffdd"><td align="right">'.
 	'<input type="hidden" name="ctr'.($ctr-1).'" value="'.$student.'" />'.
 	"\n".$ctr.'&nbsp;</td><td>&nbsp;'.
@@ -2564,7 +2668,22 @@ sub viewstudentgrade {
     foreach my $apart (@$parts) {
 	my ($part,$type) = &split_part_type($apart);
 	my $score=$record{"resource.$part.$type"};
-	$result.='<td align="middle">';
+        $result.='<td align="middle">';
+        my ($aggtries,$totaltries);
+        unless (exists($aggregates{$part})) {
+	    $totaltries = $record{'resource.'.$part.'.tries'};
+
+	    $aggtries = $totaltries;
+            if ($$last_resets{$part}) {  
+                $aggtries = &get_num_tries(\%record,$$last_resets{$part},
+					   $part);
+            }
+            $result.='<input type="hidden" name="'.
+                'GD_'.$student.'_'.$part.'_aggtries" value="'.$aggtries.'" />'."\n";
+            $result.='<input type="hidden" name="'.
+                'GD_'.$student.'_'.$part.'_totaltries" value="'.$totaltries.'" />'."\n";
+            $aggregates{$part} = 1;
+        }
 	if ($type eq 'awarded') {
 	    my $pts = $score eq '' ? '' : $score*$$weight{$part};
 	    $result.='<input type="hidden" name="'.
@@ -2679,12 +2798,13 @@ sub editgrades {
 	    $noupdate.=$line."<td colspan=\"$numcols\"><font color=\"red\">Not allowed to modify student</font></td></tr>";
 	    next;
 	}
+        my %aggregate = ();
+        my $aggregateflag = 0;
 	foreach (@partid) {
 	    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;
@@ -2707,6 +2827,13 @@ sub editgrades {
 		$newrecord{'resource.'.$_.'.awarded'} = 0;
 		$newrecord{'resource.'.$_.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
 		$updateflag = 1;
+                if ($env{'form.GD_'.$user.'_'.$_.'_aggtries'} > 0) {
+                    my $aggtries = $env{'form.GD_'.$user.'_'.$_.'_aggtries'};
+                    my $totaltries = $env{'form.GD_'.$user.'_'.$_.'_totaltries'};
+                    my $solvedstatus = $env{'form.GD_'.$user.'_'.$_.'_solved_s'};
+                    &decrement_aggs($symb,$_,\%aggregate,$aggtries,$totaltries,$solvedstatus);
+                    $aggregateflag = 1;
+                }
 	    } elsif (!($old_part eq $partial && $old_score eq $score)) {
 		$updateflag = 1;
 		$newrecord{'resource.'.$_.'.awarded'}  = $partial if $partial ne '';
@@ -2746,6 +2873,11 @@ sub editgrades {
 	    $noupdate.='<tr bgcolor="#ffffde"><td align="right">&nbsp;'.$noupdateCtr.'&nbsp;</td>'.$line;
 	    $noupdateCtr++;
 	}
+        if ($aggregateflag) {
+            &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
+                      $env{'course.'.$env{'request.course.id'}.'.domain'},
+                      $env{'course.'.$env{'request.course.id'}.'.num'});
+        }
     }
     if ($noupdate) {
 #	my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;
@@ -3566,6 +3698,8 @@ sub updateGradeByPage {
 
 	    my %newrecord=();
 	    my @displayPts=();
+            my %aggregate = ();
+            my $aggregateflag = 0;
 	    foreach my $partid (@{$parts}) {
 		my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid};
 		my $oldpts = $env{'form.oldpts'.$question.'_'.$partid};
@@ -3592,6 +3726,14 @@ sub updateGradeByPage {
 		    $newrecord{'resource.'.$partid.'.regrader'} = "$env{'user.name'}:$env{'user.domain'}";
 		    $changeflag++;
 		    $newpts = '';
+                    
+                    my $aggtries =  $env{'form.aggtries'.$question.'_'.$partid};
+                    my $totaltries = $env{'form.totaltries'.$question.'_'.$partid};
+                    my $solvedstatus = $env{'form.solved'.$question.'_'.$partid};
+                    if ($aggtries > 0) {
+                        &decrement_aggs($symbx,$partid,\%aggregate,$aggtries,$totaltries,$solvedstatus);
+                        $aggregateflag = 1;
+                    }
 		}
 		my $display_part=&get_display_part($partid,undef,
 						   $curRes->symb());
@@ -3617,6 +3759,11 @@ sub updateGradeByPage {
 		&Apache::lonnet::cstore(\%newrecord,$symbx,$env{'request.course.id'},
 					$udom,$uname);
 	    }
+            if ($aggregateflag) {
+                &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
+                      $env{'course.'.$env{'request.course.id'}.'.domain'},
+                      $env{'course.'.$env{'request.course.id'}.'.num'});
+            }
 
 	    $studentTable.='<td valign="top">'.$displayPts[0].'</td>'.
 		'<td valign="top">'.$displayPts[1].'</td>'.