--- loncom/homework/grades.pm	2005/04/07 07:03:23	1.258
+++ 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.258 2005/04/07 07:03:23 albertel 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
@@ -2109,6 +2126,7 @@ sub processHandGrade {
 #---- Save the score and award for each student, if changed
 sub saveHandGrade {
     my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_;
+    my @v_flag;
     my $usec = &Apache::lonnet::getsection($domain,$stuname,
 					   $env{'request.course.id'});
     if (!&canmodify($usec)) { return('not_allowed'); }
@@ -2116,45 +2134,62 @@ sub saveHandGrade {
     my @parts_graded;
     my %newrecord  = ();
     my ($pts,$wgt) = ('','');
-    foreach (split(/:/,$env{'form.partlist'.$newflg})) {
+    my %aggregate = ();
+    my $aggregateflag = 0;
+    foreach my $new_part (split(/:/,$env{'form.partlist'.$newflg})) {
 	#collaborator may vary for different parts
-	if ($submitter && $_ ne $part) { next; }
-	my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$_};
+	if ($submitter && $new_part ne $part) { next; }
+	my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$new_part};
 	if ($dropMenu eq 'excused') {
-	    if ($record{'resource.'.$_.'.solved'} ne 'excused') {
-		$newrecord{'resource.'.$_.'.solved'} = 'excused';
-		if (exists($record{'resource.'.$_.'.awarded'})) {
-		    $newrecord{'resource.'.$_.'.awarded'} = '';
+	    if ($record{'resource.'.$new_part.'.solved'} ne 'excused') {
+		$newrecord{'resource.'.$new_part.'.solved'} = 'excused';
+		if (exists($record{'resource.'.$new_part.'.awarded'})) {
+		    $newrecord{'resource.'.$new_part.'.awarded'} = '';
 		}
-	    $newrecord{'resource.'.$_.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
+	    $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
 	    }
 	} elsif ($dropMenu eq 'reset status'
-		 && exists($record{'resource.'.$_.'.solved'})) { #don't bother if no old records -> no attempts
+		 && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts
 	    foreach my $key (keys (%record)) {
-		if ($key=~/^resource\.\Q$_\E\./) { $newrecord{$key} = ''; }
+		if ($key=~/^resource\.\Q$new_part\E\./) { $newrecord{$key} = ''; }
 	    }
-	    $newrecord{'resource.'.$_.'.regrader'}=
+	    $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.'_'.$_} ne '' ? 
-		    $env{'form.GD_BOX'.$newflg.'_'.$_} : 
-		    $env{'form.RADVAL'.$newflg.'_'.$_});
-	    if ($pts eq '' && $env{'form.GD_SEL'.$newflg.'_'.$_} eq '') {
+	    $pts = ($env{'form.GD_BOX'.$newflg.'_'.$new_part} ne '' ? 
+		    $env{'form.GD_BOX'.$newflg.'_'.$new_part} : 
+		    $env{'form.RADVAL'.$newflg.'_'.$new_part});
+	    if ($pts eq '' && $env{'form.GD_SEL'.$newflg.'_'.$new_part} eq '') {
 		next;
 	    }
-	    $wgt = $env{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 : 
-		$env{'form.WGT'.$newflg.'_'.$_};
+	    $wgt = $env{'form.WGT'.$newflg.'_'.$new_part} eq '' ? 1 : 
+		$env{'form.WGT'.$newflg.'_'.$new_part};
 	    my $partial= $pts/$wgt;
-	    if ($partial eq $record{'resource.'.$_.'.awarded'}) {
+	    if ($partial eq $record{'resource.'.$new_part.'.awarded'}) {
 		#do not update score for part if not changed.
 		next;
 	    } else {
-	        push @parts_graded, $_;
+	        push @parts_graded, $new_part;
 	    }
-	    if ($record{'resource.'.$_.'.awarded'} ne $partial) {
-		$newrecord{'resource.'.$_.'.awarded'}  = $partial;
+	    if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {
+		$newrecord{'resource.'.$new_part.'.awarded'}  = $partial;
 	    }
-	    my $reckey = 'resource.'.$_.'.solved';
+	    my $reckey = 'resource.'.$new_part.'.solved';
 	    if ($partial == 0) {
 		if ($record{$reckey} ne 'incorrect_by_override') {
 		    $newrecord{$reckey} = 'incorrect_by_override';
@@ -2165,32 +2200,107 @@ sub saveHandGrade {
 		}
 	    }	    
 	    if ($submitter && 
-		($record{'resource.'.$_.'.submitted_by'} ne $submitter)) {
-		$newrecord{'resource.'.$_.'.submitted_by'} = $submitter;
+		($record{'resource.'.$new_part.'.submitted_by'} ne $submitter)) {
+		$newrecord{'resource.'.$new_part.'.submitted_by'} = $submitter;
 	    }
-	    $newrecord{'resource.'.$_.'.regrader'}=
+	    $newrecord{'resource.'.$new_part.'.regrader'}=
 		"$env{'user.name'}:$env{'user.domain'}";
 	}
+	# unless problem has been graded, set flag to version the submitted files
+	unless ($record{'resource.'.$new_part.'.solved'} =~ /^correct_/  || $record{'resource.'.$new_part.'.solved'} eq 'incorrect_by_override') {
+	    push (@v_flag,$new_part);
+	}
     }
     if (scalar(keys(%newrecord)) > 0) {
-        &version_portfiles(\%record, \@parts_graded, $env{'request.course.id'}, $symb, $domain, $stuname);
+        if (scalar(@v_flag)) {
+            &version_portfiles(\%record, \@parts_graded, $env{'request.course.id'}, $symb, $domain, $stuname, \@v_flag);
+        }
 	&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) = @_;
+    my ($record, $parts_graded, $courseid, $symb, $domain, $stuname, $v_flag) = @_;
+    my $version_parts = join('|',@$v_flag);
     my $parts = join('|', @$parts_graded);
     my $portfolio_root = &Apache::loncommon::propath($domain,
 						 $stuname).
 						'/userfiles/portfolio';
     foreach my $key(keys %$record) {
-        if ($key =~ /^resource\.($parts)\./ && $key =~ /\.portfiles$/) {
+        my $new_portfiles;
+        if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {
+            my @v_portfiles;
             my @portfiles = split(/,/,$$record{$key});
+            # &Apache::lonnet::logthis("should be unmarking and remarking");
             foreach my $file (@portfiles) {
+                &Apache::lonnet::unmark_as_readonly($domain,$stuname,[$symb,$env{'request.course.id'}],$file);
                 my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*$)/);
                 my $version = 0;
                 my @answer_file_parts = split(/\./, $answer_file);
@@ -2212,20 +2322,22 @@ sub version_portfiles {
                 }
                 $version++;
                 my $home_server = &Apache::lonnet::homeserver($stuname,$domain,undef);
-                $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stuname/$directory$answer_file");
-                # $env{'form.copy.filename'}='';
-                my $copy_result = &Apache::lonnet::finishuserfileupload($stuname,$domain,$home_server,'copy',
+                $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stuname/portfolio$directory$answer_file");
+                if($env{'form.copy'} eq '-1') {
+                    &Apache::lonnet::logthis('problem getting file '.$directory.$answer_file);
+                } else {
+                   my $copy_result = &Apache::lonnet::finishuserfileupload($stuname,$domain,$home_server,'copy',
                                     '/portfolio'.$directory.$answer_file_parts[0].'.'.$version.'.'.$answer_file_parts[-1]);
-                &Apache::lonnet::logthis('copy result is '.$copy_result);
-                &Apache::lonnet::logthis('answer file is '.$answer_file.
-                        ' becomes '.$answer_file_parts[0].'.'.$version.'.'.$answer_file_parts[-1]);
-                &Apache::lonnet::logthis('from dir list is '.$file_names[0].' has '.@file_name_parts.' parts');
+                    push(@v_portfiles, $answer_file_parts[0].'.'.$version.'.'.$answer_file_parts[-1]);
+                    &Apache::lonnet::mark_as_readonly($domain,$stuname,
+                                ['/portfolio'.$directory.$answer_file_parts[0].'.'.$version.'.'.$answer_file_parts[-1]],
+                                [$symb,$env{'request.course.id'},'graded']);
+                }
             }
-            &Apache::lonnet::logthis('found key portfiles '.$key);
-            &Apache::lonnet::logthis('found value portfiles '.$$record{$key});
+            $$record{$key} = join(',',@v_portfiles);
         }
     } 
-    
+    return 'ok';   
     
 }
 
@@ -2294,6 +2406,7 @@ sub viewgrades_js {
     function writeRadText(partid,weight) {
 	var selval   = document.classgrade["SELVAL_"+partid];
 	var radioButton = document.classgrade["RADVAL_"+partid];
+        var override = document.classgrade["FORCE_"+partid].checked;
 	var textbox = document.classgrade["TEXTVAL_"+partid];
 	if (selval[1].selected || selval[2].selected) {
 	    for (var i=0; i<radioButton.length; i++) {
@@ -2307,7 +2420,7 @@ sub viewgrades_js {
 		var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
 		var saveval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
 		var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
-		if (saveval != "correct") {
+		if ((saveval != "correct") || override) {
 		    scorename.value = "";
 		    if (selval[1].selected) {
 			selname[1].selected = true;
@@ -2324,7 +2437,7 @@ sub viewgrades_js {
 		var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
 		var saveval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
 		var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
-		if (saveval != "correct") {
+		if ((saveval != "correct") || override) {
 		    scorename.value = document.classgrade["GD_"+user+"_"+partid+"_awarded_s"].value;
 		    selname[0].selected = true;
 		}
@@ -2361,7 +2474,7 @@ sub viewgrades_js {
 	    if (selval[2].selected) {
 		document.classgrade["GD_"+user+'_'+partid+"_tries"].value = "0";
 	    }
-	}
+        }
     }
 
     function resetEntry(numpart) {
@@ -2478,7 +2591,8 @@ sub viewgrades {
 		$weight{$partid}.')"> '.
 	    '<option selected="on"> </option>'.
 	    '<option>excused</option>'.
-	    '<option>reset status</option></select></td></tr>'."\n";
+	    '<option>reset status</option></select></td>'.
+            '<td><label><input type="checkbox" name="FORCE_'.$partid.'" /> Override "Correct"</label></td></tr>'."\n";
 	$ctsparts++;
     }
     $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n".
@@ -2493,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.
@@ -2511,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');
@@ -2518,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";
@@ -2535,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;'.
@@ -2548,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="'.
@@ -2663,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;
@@ -2691,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 '';
@@ -2730,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;
@@ -3550,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};
@@ -3576,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());
@@ -3601,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>'.
@@ -3771,7 +3934,7 @@ sub scantron_selectphase {
           </tr>
           <tr bgcolor="#ffffe6">
             <td colspan="2">
-              <input type="submit" value="Validate Scantron Records" />
+              <input type="submit" value="Grading: Validate Scantron Records" />
             </td>
           </tr>
         </table>
@@ -4226,10 +4389,10 @@ sub scantron_do_warning {
 	    $r->print('<p><font color="red">You have not selected a the format of the student\'s response data.</font></p>');
 	} 
     } else {
-	my $warning=&scantron_warning_screen('Validate Records');
+	my $warning=&scantron_warning_screen('Grading: Validate Records');
 	$r->print(<<STUFF);
 $warning
-<input type="submit" name="submit" value="Validate Records" />
+<input type="submit" name="submit" value="Grading: Validate Records" />
 <input type="hidden" name="command" value="scantron_validate" />
 STUFF
     }