--- loncom/homework/grades.pm	2002/10/16 19:13:57	1.55
+++ loncom/homework/grades.pm	2002/11/27 16:39:37	1.62
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.55 2002/10/16 19:13:57 matthew Exp $
+# $Id: grades.pm,v 1.62 2002/11/27 16:39:37 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -111,62 +111,32 @@ sub response_type {
 #--- section, ids and fullnames for each user.
 sub getclasslist {
     my ($getsec,$hideexpired) = @_;
-    my $now = time;
-    my %classlist=&Apache::lonnet::dump('classlist',
-					$ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
-					$ENV{'course.'.$ENV{'request.course.id'}.'.num'});
-    my ($tmp) = keys(%classlist);
+    my $classlist=&Apache::loncoursedata::get_classlist();
     # Bail out if we were unable to get the classlist
-    return if ($tmp =~ /^(con_lost|error|no_such_host)/i);
-
-    # codes to check for fields in the classlist
-    # should contain end:start:id:section:fullname
-    for (keys %classlist) {
-	my (@fields) = split(/:/,$classlist{$_});
-	%classlist   = &reformat_classlist(\%classlist) if (scalar(@fields) <= 2);
-	last;
-    }
-
-    my (@holdsec,@sections,%allids,%stusec,%fullname);
-    foreach (keys(%classlist)) {
-	my ($end,$start,$id,$section,$fullname)=split(/:/,$classlist{$_});
+    return if (! defined($classlist));
+    #
+    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->{$_}};
 	# still a student?
-	if (($hideexpired) && ($end) && ($end < $now)) {
-	    next;
-	}
+	if (($hideexpired) && ($status ne 'Active')) {
+            delete ($classlist->{$_});
+            next;
+        }
 	$section = ($section ne '' ? $section : 'no');
-	push @holdsec,$section;
 	if ($getsec eq 'all' || $getsec eq $section) {
-	    push (@{ $classlist{$getsec} }, $_);
-	    $allids{$_}  =$id;
-	    $stusec{$_}  =$section;
-	    $fullname{$_}=$fullname;
-	}
+            $sections{$section}++;
+            $fullnames{$_}=$fullname;
+        } else {
+            delete($classlist->{$_});
+        }
     }
     my %seen = ();
-    foreach my $item (@holdsec) {
-	push (@sections, $item) unless $seen{$item}++;
-    }
-    return (\%classlist,\@sections,\%allids,\%stusec,\%fullname);
-}
-
-# add id, section and fullname to the classlist.db
-# done to maintain backward compatibility with older versions
-sub reformat_classlist {
-    my ($classlist) = shift;
-    foreach (sort keys(%$classlist)) {
-	my ($unam,$udom) = split(/:/);
-	my $section      = &Apache::lonnet::usection($udom,$unam,$ENV{'request.course.id'});
-	my $fullname     = &get_fullname ($unam,$udom);
-	my %userid       = &Apache::lonnet::idrget($udom,($unam));
-	$$classlist{$_}  = $$classlist{$_}.':'.$userid{$unam}.':'.$section.':'.$fullname;
-    }
-    my $putresult = &Apache::lonnet::put
-	('classlist',\%$classlist,
-	 $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
-	 $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
-
-    return %$classlist;
+    my @sections = sort(keys(%sections));
+    return ($classlist,\@sections,\%fullnames);
 }
 
 #find user domain
@@ -267,8 +237,8 @@ sub verifyreceipt {
 	'<font size=+1><b>Resource: </b>'.$ENV{'form.url'}.'</font><br><br>'."\n";
 
     my ($string,$contents,$matches) = ('','',0);
-    my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist('all','0');
-    
+    my (undef,undef,$fullname) = &getclasslist('all','0');
+
     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
 	my ($uname,$udom)=split(/\:/);
 	if ($receipt eq 
@@ -369,8 +339,9 @@ LISTJAVASCRIPT
     my $checklastsub = $ENV{'form.handgrade'} eq 'yes' ? '' : 'checked';
 
     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'."\n".
-	'&nbsp;<b>View Problem: </b><input type="radio" name="vProb" value="no" checked> no '."\n".
-	'<input type="radio" name="vProb" value="yes"> yes <br />'."\n".
+	'&nbsp;<b>View Problem: </b><input type="radio" name="vProb" value="no" /> no '."\n".
+	'<input type="radio" name="vProb" value="yes" checked /> one student '."\n".
+	'<input type="radio" name="vProb" value="all" /> all students <br />'."\n".
 	'&nbsp;<b>Submissions: </b>'."\n";
     if ($ENV{'form.handgrade'} eq 'yes') {
 	$gradeTable.='<input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> handgrade only'."\n";
@@ -381,7 +352,7 @@ LISTJAVASCRIPT
 	'<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".
 	'<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".
 	'<input type="hidden" name="response"    value="'.$ENV{'form.response'}.'" />'."\n".
-	'<input type="hidden" name="handgrade"   value="'.$ENV{'form.handgrade'}.'" /><br />'."\n".
+	'<input type="hidden" name="handgrade": #falue="'.$ENV{'form.handgrade'}.'" /><br />'."\n".
 	'<input type="hidden" name="showgrading" value="'.$ENV{'form.showgrading'}.'" /><br />'."\n".
 	'<input type="hidden" name="url"  value="'.$url.'" />'."\n".
 	'<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
@@ -393,7 +364,7 @@ LISTJAVASCRIPT
 	'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
 	'value="'.$viewgrade.'" />'."\n";
  
-    my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($getsec,'0');
+    my (undef,undef,$fullname) = &getclasslist($getsec,'0');
     
     $gradeTable.='<table border="0"><tr><td bgcolor="#777777">'.
 	'<table border="0"><tr bgcolor="#e6ffff">'.
@@ -701,21 +672,21 @@ sub sub_page_js {
     pWin.document.write("<tr bgcolor=\\"#ffffdd\\">");
     pWin.document.write("<td>Subject</td>");
     pWin.document.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
-    pWin.document.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+" \\"size=\\"60\\" maxlength=\\"80\\"></td></tr>");
+    pWin.document.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"60\\" maxlength=\\"80\\"></td></tr>");
 }
 
 function displaySavedMsg(ctr,msg,shwsel) {
     pWin.document.write("<tr bgcolor=\\"#ffffdd\\">");
     pWin.document.write("<td align=\\"center\\">"+ctr+"</td>");
     pWin.document.write("<td align=\\"center\\"><input name=\\"msgn"+ctr+"\\" type=\\"checkbox\\"" +shwsel+"></td>");
-    pWin.document.write("<td><input name=\\"msg"+ctr+"\\" type=\\"text\\" value=\\""+msg+" \\" size=\\"60\\" maxlength=\\"80\\"></td></tr>");
+    pWin.document.write("<td><input name=\\"msg"+ctr+"\\" type=\\"text\\" value=\\""+msg+"\\" size=\\"60\\" maxlength=\\"80\\"></td></tr>");
 }
 
   function newMsg(newmsg,shwsel) {
     pWin.document.write("<tr bgcolor=\\"#ffffdd\\">");
     pWin.document.write("<td align=\\"center\\">New</td>");
     pWin.document.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
-    pWin.document.write("<td><input name=\\"newmsg\\" type=\\"text\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" value=\\""+newmsg+" \\" size=\\"60\\" maxlength=\\"80\\"></td></tr>");
+    pWin.document.write("<td><input name=\\"newmsg\\" type=\\"text\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" value=\\""+newmsg+"\\" size=\\"60\\" maxlength=\\"80\\"></td></tr>");
 }
 
   function msgTail() {
@@ -825,6 +796,32 @@ SUBJAVASCRIPT
 }
 
 
+sub show_problem {
+    my ($request,$symb,$uname,$udom,$removeform) = @_;
+    my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
+						      $ENV{'request.course.id'});
+    if ($removeform) {
+	$rendered=~s|<form(.*?)>||g;
+	$rendered=~s|</form>||g;
+	$rendered=~s|name="submit"|name="would_have_been_submit"|g;
+    }
+    my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,
+							   $ENV{'request.course.id'});
+    if ($removeform) {
+	$companswer=~s|<form(.*?)>||g;
+	$companswer=~s|</form>||g;
+	$rendered=~s|name="submit"|name="would_have_been_submit"|g;
+    }
+    my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';
+    $result.='<table border="0" width="100%"><tr><td bgcolor="#e6ffff">';
+    $result.='<b> View of the problem - '.$ENV{'form.fullname'}.
+	'</b></td></tr><tr><td bgcolor="#ffffff">'.$rendered.'<br />';
+    $result.='<b>Correct answer:</b><br />'.$companswer;
+    $result.='</td></tr></table>';
+    $result.='</td></tr></table><br />';
+    $request->print($result);
+}
+
 # --------------------------- show submissions of a student, option to grade 
 sub submission {
     my ($request,$counter,$total) = @_;
@@ -839,8 +836,7 @@ sub submission {
     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 ''; }
     my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');
-    $ENV{'form.vProb'} = $ENV{'form.vProb'} ne '' ? $ENV{'form.vProb'} : 'yes';
-    my ($classlist,$seclist,$ids,$stusec,$fullname);
+#    $ENV{'form.vProb'} = $ENV{'form.vProb'} ne '' ? $ENV{'form.vProb'} : 'yes';
 
     # header info
     if ($counter == 0) {
@@ -851,18 +847,7 @@ sub submission {
 	# option to display problem, only once else it cause problems 
         # with the form later since the problem has a form.
 	if ($ENV{'form.vProb'} eq 'yes') {
-	    my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
-							      $ENV{'request.course.id'});
-	    my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,
-								   $ENV{'request.course.id'});
-	    my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';
-	    $result.='<table border="0" width="100%"><tr><td bgcolor="#e6ffff">';
-	    $result.='<b> View of the problem - '.$ENV{'form.fullname'}.
-		'</b></td></tr><tr><td bgcolor="#ffffff">'.$rendered.'<br />';
-	    $result.='<b>Correct answer:</b><br />'.$companswer;
-	    $result.='</td></tr></table>';
-	    $result.='</td></tr></table><br />';
-	    $request->print($result);
+	    &show_problem($request,$symb,$uname,$udom,0);
 	}
 	
 	# kwclr is the only variable that is guaranteed to be non blank 
@@ -925,6 +910,11 @@ KEYWORDS
         }
     }
 
+    if ($ENV{'form.vProb'} eq 'all') {
+	$request->print('<br /><br /><br />');
+	&show_problem($request,$symb,$uname,$udom,1);
+    }
+
     my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
     my ($partlist,$handgrade) = &response_type($url);
 
@@ -941,49 +931,61 @@ KEYWORDS
 
     # If this is handgraded, then check for collaborators
     my @col_fullnames;
+    my ($classlist,$fullname);
     if ($ENV{'form.handgrade'} eq 'yes') {
 	my @col_list;
-	($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist('all','0');
+	($classlist,undef,$fullname) = &getclasslist('all','0');
 	for (keys (%$handgrade)) {
 	    my $ncol = &Apache::lonnet::EXT('resource.'.$_.
-					    '.maxcollaborators',$symb,$udom,$uname);
-	    if ($ncol > 0) {
-		s/\_/\./g;
-		if ($record{'resource.'.$_.'.collaborators'} ne '') {
-		    my (@collaborators) = split(/,?\s+/,
-						$record{'resource.'.$_.'.collaborators'});
-		    my (@badcollaborators);
-		    if (scalar(@collaborators) != 0) {
-			$result.='<b>Collaborators: </b>';
-			foreach my $collaborator (@collaborators) {
-			    $collaborator = $collaborator =~ /\@|:/ ? 
-				(split(/@|:/,$collaborator))[0] : $collaborator;
-			    next if ($collaborator eq $uname);
-			    if (!grep /^$collaborator:/i,keys %$classlist) {
-				push @badcollaborators,$collaborator;
-				next;
-			    }
-			    push @col_list, $collaborator;
-			    my ($lastname,$givenn) = split(/,/,$$fullname{$collaborator.':'.$udom});
-			    push @col_fullnames, $givenn.' '.$lastname;
-			    $result.=$$fullname{$collaborator.':'.$udom}.'&nbsp; &nbsp; &nbsp;';
-			}
-			$result.='<br />'."\n";
-			$result.='<table border="0"><tr bgcolor="#ffbbbb"><td>'.
-			    'This student has submitted '.
-			    (scalar (@badcollaborators) > 1 ? '' : 'an').
-			    ' invalid collaborator'.(scalar (@badcollaborators) > 1 ? 's. ' : '. ').
-			    (join ', ',@badcollaborators).'</td></tr></table>' 
-			    if (scalar(@badcollaborators) > 0);
-
-			$result.='<table border="0"><tr bgcolor="#ffbbbb"><td>'.
-			    'This student has submitted too many collaborators. Maximum is '.
-			    $ncol.'.</td></tr></table>' if (scalar(@collaborators) > $ncol);
-			$result.='<input type="hidden" name="collaborator'.$counter.
-			    '" value="'.(join ':',@col_list).'" />'."\n";
-		    }
-		}
-	    }
+					    '.maxcollaborators',
+                                            $symb,$udom,$uname);
+	    next if ($ncol <= 0);
+            s/\_/\./g;
+            next if ($record{'resource.'.$_.'.collaborators'} eq '');
+            my (@collaborators) = split(/,?\s+/,
+                                   $record{'resource.'.$_.'.collaborators'});
+            my (@badcollaborators);
+            if (scalar(@collaborators) != 0) {
+                $result.='<b>Collaborators: </b>';
+                foreach my $collaborator (@collaborators) {
+                    my ($co_name,$co_dom) = split /\@|:/,$collaborator;
+                    $co_dom = $udom if (! defined($co_dom));
+                    next if ($co_name eq $uname && $co_dom eq $udom);
+                    # Doing this grep allows 'fuzzy' specification
+                    my @Matches = grep /^$co_name:$co_dom/i,
+                    keys %$classlist;
+                    if (! scalar(@Matches)) {
+                        push @badcollaborators,$collaborator;
+                        next;
+                    }
+                    push @col_list, @Matches;
+                    foreach (@Matches) {
+                        my ($lastname,$givenn) = split(/,/,$$fullname{$_});
+                        push @col_fullnames, $givenn.' '.$lastname;
+                        $result.=$$fullname{$_}.'&nbsp; &nbsp; &nbsp;';
+                    }
+                }
+                $result.='<br />'."\n";
+                if (scalar(@badcollaborators) > 0) {
+                    $result.='<table border="0"><tr bgcolor="#ffbbbb"><td>';
+                    $result.='This student has submitted ';
+                    if (scalar(@badcollaborators) == 1) {
+                        $result .= 'an invalid collaborator';
+                    } else {
+                        $result .= 'invalid collaborators';
+                    }
+                    $result .= ': '.join(', ',@badcollaborators);
+                    
+                }
+                if (scalar(@collaborators > $ncol)) {
+                    $result .= '<table border="0"><tr bgcolor="#ffbbbb"><td>';
+                    $result .= 'This student has sumbitted too many '.
+                        'collaborators.  Maximum is '.$ncol;
+                    $result .= '</td></tr></table>';
+                }
+                $result.='<input type="hidden" name="collaborator'.$counter.
+                    '" value="'.(join ':',@col_list).'" />'."\n";
+            }
 	}
     }
     $request->print($result."\n");
@@ -1107,9 +1109,15 @@ KEYWORDS
 	    'onChange="javascript:clearRadBox(this.form.RADVAL'.$counter.'_'.$partid.
 	    ',this.form.GD_BOX'.$counter.'_'.$partid.
 	    ',this.form.GD_SEL'.$counter.'_'.$partid.
-	    ',this.form.stores'.$counter.'_'.$partid.')" />'."\n".
-	    '<option selected="on"> </option>'.
-	    '<option>excused</option></select>'."&nbsp&nbsp\n";
+	    ',this.form.stores'.$counter.'_'.$partid.')" >'."\n";
+	if ($record{'resource.'.$partid.'.solved'} eq 'excused') {
+	    $result.='<option> </option>'.
+		'<option selected="on">excused</option></select>';
+	} else {
+	    $result.='<option selected="on"> </option>'.
+		'<option>excused</option></select>';
+	}
+	$result.="&nbsp&nbsp\n";
 	$result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="0" />';
 	$result.='</td></tr></table>'."\n";
 	$request->print($result);
@@ -1165,9 +1173,9 @@ sub get_last_submission {
 	for ($version=1;$version<=$returnhash{'version'};$version++) {
 	    foreach (sort(split(/\:/,$returnhash{$version.':keys'}))) {
 		$lasthash{$_}=$returnhash{$version.':'.$_};
-		if ($returnhash{$version.':'.$_} =~ /(SUBMITTED|DRAFT)$/) {
+#		if ($returnhash{$version.':'.$_} =~ /(SUBMITTED|DRAFT)$/) {
 		   $timestamp = scalar(localtime($returnhash{$version.':timestamp'}));
-	       } 
+#	       } 
 	    }
 	}
 	foreach ((keys %lasthash)) {
@@ -1191,8 +1199,13 @@ sub keywords_highlight {
     (my $styleoff = $styleon) =~ s/\</\<\//;
     my @keylist   = split(/[,\s+]/,$ENV{'form.keywords'});
     foreach (@keylist) {
-	$string =~ s/\b$_(\b|\.)/\<font color\=$ENV{'form.kwclr'} $size\>$styleon$_$styleoff\<\/font\>/gi;
+	$string =~ s/\b\Q$_\E(\b|\.)/\<font color\=$ENV{'form.kwclr'} $size\>$styleon$_$styleoff\<\/font\>/gi;
     }
+    # This is not really the right place to do this, but I cannot find a
+    # better one at this time.  So here we go - the m in the s:::mg causes
+    # ^ to match the beginning of a new line.  So we replace(???) the beginning
+    # of the line with <br /> to make things formatted a little better.
+    $string =~ s:^:<br />:mg;
     return $string;
 }
 
@@ -1213,7 +1226,7 @@ sub processHandGrade {
 
 	    my $includemsg = $ENV{'form.includemsg'.$ctr};
 	    my ($subject,$message,$msgstatus) = ('','','');
-	    if ($includemsg =~ /savemsg|new$ctr/) {
+	    if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) {
 		$subject = $ENV{'form.msgsub'} if ($includemsg =~ /^msgsub/);
 		my (@msgnum) = split(/,/,$includemsg);
 		foreach (@msgnum) {
@@ -1301,7 +1314,7 @@ sub processHandGrade {
 	$laststu = $firststu if ($ctr > $ngrade);
     }
 
-    my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0');
+    my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
     my (@parsedlist,@nextlist);
     my ($nextflg) = 0;
     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
@@ -1363,8 +1376,12 @@ sub saveHandGrade {
     my %newrecord;
     foreach (split(/:/,$ENV{'form.partlist'.$newflg})) {
 	if ($ENV{'form.GD_SEL'.$newflg.'_'.$_} eq 'excused') {
-	    $newrecord{'resource.'.$_.'.solved'} = 'excused' 
-		if ($record{'resource.'.$_.'.solved'} ne 'excused');
+	    if ($record{'resource.'.$_.'.solved'} ne 'excused') {
+		$newrecord{'resource.'.$_.'.solved'} = 'excused';
+		if (exists($record{'resource.'.$_.'.awarded'})) {
+		    $newrecord{'resource.'.$_.'.awarded'} = '';
+		}
+	    }
 	} else {
 	    my $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ? 
 		       $ENV{'form.GD_BOX'.$newflg.'_'.$_} : 
@@ -1640,7 +1657,7 @@ sub viewgrades {
 	    $weight{$partid}.' (problem weight)</td>'."\n";
 	$result.= '</td><td><select name="SELVAL_'.$partid.'"'.
 	    'onChange="javascript:writeRadText(\''.$partid.'\','.
-		$weight{$partid}.')" /> '.
+		$weight{$partid}.')"> '.
 	    '<option selected="on"> </option>'.
 	    '<option>excused</option></select></td></tr>'."\n";
 	$ctsparts++;
@@ -1682,7 +1699,7 @@ sub viewgrades {
 
     #get info for each student
     #list all the students - with points and grade status
-    my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0');
+    my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
     my $ctr = 0;
     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
 	my ($uname,$udom) = split(/:/);
@@ -1767,7 +1784,7 @@ sub editgrades {
 		    'ungraded' =>'ungraded_attempted',
 		    'nothing'  => '',
 		    );
-    my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0');
+    my ($classlist,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
 
     my (@partid);
     my %weight = ();
@@ -2185,7 +2202,7 @@ sub gradingmenu {
 #--- Menu for grading a section or the whole class ---
 sub view_edit_entire_class_form {
     my ($symb,$url)=@_;
-    my ($classlist,$sections) = &getclasslist('all','0');
+    my ($classlist,$sections,undef) = &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.='&nbsp;<b>Grade Entire Section or Class</b></td></tr>'."\n";