--- loncom/homework/grades.pm	2003/11/04 19:38:09	1.146
+++ loncom/homework/grades.pm	2003/11/07 18:05:33	1.149
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.146 2003/11/04 19:38:09 albertel Exp $
+# $Id: grades.pm,v 1.149 2003/11/07 18:05:33 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -133,9 +133,9 @@ sub response_type {
     $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url))) if ($symb eq '');
     my $allkeys = &Apache::lonnet::metadata($url,'keys');
     my %seen = ();
-    my (@partlist,%handgrade);
+    my (@partlist,%handgrade,%responseType);
     foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) {
-	if (/^\w+response_\w+.*/) {
+	if (/^\w+response_.*/) {
 	    my ($responsetype,$part) = split(/_/,$_,2);
 	    my ($partid,$respid) = split(/_/,$part);
 	    if (&Apache::loncommon::check_if_partid_hidden($partid,$symb)) {
@@ -143,13 +143,15 @@ sub response_type {
 	    }
 	    $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!!
 	    my ($value) = &Apache::lonnet::EXT('resource.'.$part.'.handgrade',$symb);
-	    $handgrade{$part} = $responsetype.':'.($value eq 'yes' ? 'yes' : 'no'); 
+	    $handgrade{$part} = ($value eq 'yes' ? 'yes' : 'no'); 
+	    if (!exists($responseType{$partid})) { $responseType{$partid}={}; }
+	    $responseType{$partid}->{$respid}=$responsetype;
 	    next if ($seen{$partid} > 0);
 	    $seen{$partid}++;
 	    push @partlist,$partid;
 	}
     }
-    return \@partlist,\%handgrade;
+    return \@partlist,\%handgrade,\%responseType;
 }
 
 #--- Show resource title
@@ -158,42 +160,104 @@ sub showResourceInfo {
     my ($url,$probTitle) = @_;
     my $result ='<table border="0">'.
 	'<tr><td colspan=3><font size=+1><b>Current Resource: </b>'.$probTitle.'</font></td></tr>'."\n";
-    my ($partlist,$handgrade) = &response_type($url);
+    my ($partlist,$handgrade,$responseType) = &response_type($url);
     my %resptype = ();
     my $hdgrade='no';
-    for (sort keys(%$handgrade)) {
-	my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
-	my $partID = (split(/_/))[0];
-	$resptype{$partID} = $responsetype;
+    for my $part_resID (sort keys(%$handgrade)) {
+	my $handgrade=$$handgrade{$part_resID};
+	my ($partID,$resID) = split(/_/,$part_resID);
+	my $responsetype = $responseType->{$partID}->{$resID};
 	$hdgrade = $handgrade if ($handgrade eq 'yes');
-	$result.='<tr><td><b>Part </b>'.$partID.'</td>'.
+	$result.='<tr><td><b>Part </b>'.$partID.' <font color="#999999">'.
+	    $resID.'</font></td>'.
 	    '<td><b>Type: </b>'.$responsetype.'</td></tr>';
 #	    '<td><b>Handgrade: </b>'.$handgrade.'</td></tr>';
     }
     $result.='</table>'."\n";
-    return $result,\%resptype,$hdgrade,$partlist,$handgrade;
+    return $result,$responseType,$hdgrade,$partlist,$handgrade;
 }
 
+
+sub get_order {
+    my ($partid,$respid,$symb,$uname,$udom)=@_;
+    my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
+    $url=&Apache::lonnet::clutter($url);
+    my $subresult=&Apache::lonnet::ssi($url,
+				       ('grade_target' => 'analyze'),
+				       ('grade_domain' => $udom),
+				       ('grade_symb' => $symb),
+				       ('grade_courseid' => 
+					        $ENV{'request.course.id'}),
+				       ('grade_username' => $uname));
+    (undef,$subresult)=split(/_HASH_REF__/,$subresult,2);
+    my %analyze=&Apache::lonnet::str2hash($subresult);
+    return ($analyze{"$partid.$respid.shown"});
+}
 #--- Clean response type for display
-#--- Currently filters option response type only.
+#--- Currently filters option/rank/radiobutton/match/essay response types only.
 sub cleanRecord {
-    my ($answer,$response,$symb) = @_;
-    if ($response eq 'option') {
-	my (@IDs,@ans);
-	foreach (split(/\&/,&Apache::lonnet::unescape($answer))) {
-	    my ($optionID,$ans) = split(/=/);
-	    push @IDs,$optionID.'</font>';
-	    push @ans,$ans;
+    my ($answer,$response,$symb,$partid,$respid,$record,$order,$version) = @_;
+    my $grayFont = '<font color="#999999">';
+    if ($response =~ /^(option|rank)$/) {
+	my %answer=&Apache::lonnet::str2hash($answer);
+	my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
+	my ($toprow,$bottomrow);
+	foreach my $foil (@$order) {
+	    if ($grading{$foil} == 1) {
+		$toprow.='<td><b>'.$answer{$foil}.'&nbsp;</b></td>';
+	    } else {
+		$toprow.='<td><i>'.$answer{$foil}.'&nbsp;</i></td>';
+	    }
+	    $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';
 	}
-	my $grayFont = '<font color="#999999">';
 	return '<blockquote><table border="1">'.
-	    '<tr valign="top"><td>Answer</td><td>'.
-	    (join '</td><td>',@ans).'</td></tr>'.
-	    '<tr valign="top"><td>'.$grayFont.'Option ID</font></td><td>'.$grayFont.
-	    (join '</td><td>'.$grayFont,@IDs).'</font></td></tr>'.
-	    '</table></blockquote>';
-    }
-    if ($response eq 'essay') {
+	    '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
+	    '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.
+	    $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
+    } elsif ($response eq 'match') {
+	my %answer=&Apache::lonnet::str2hash($answer);
+	my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
+	my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"});
+	my ($toprow,$middlerow,$bottomrow);
+	foreach my $foil (@$order) {
+	    my $item=shift(@items);
+	    if ($grading{$foil} == 1) {
+		$toprow.='<td><b>'.$item.'&nbsp;</b></td>';
+		$middlerow.='<td><b>'.$grayFont.$answer{$foil}.'&nbsp;</font></b></td>';
+	    } else {
+		$toprow.='<td><i>'.$item.'&nbsp;</i></td>';
+		$middlerow.='<td><i>'.$grayFont.$answer{$foil}.'&nbsp;</font></i></td>';
+	    }
+	    $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';
+	}
+	return '<blockquote><table border="1">'.
+	    '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
+	    '<tr valign="top"><td>'.$grayFont.'Item ID</font></td>'.
+	    $middlerow.'</tr>'.
+	    '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.
+	    $bottomrow.'</tr>'.'</table></blockquote>';
+    } elsif ($response eq 'radiobutton') {
+	my %answer=&Apache::lonnet::str2hash($answer);
+	my ($toprow,$bottomrow);
+	my $correct=($order->[0])+1;
+	for (my $i=1;$i<=$#$order;$i++) {
+	    my $foil=$order->[$i];
+	    if (exists($answer{$foil})) {
+		if ($i == $correct) {
+		    $toprow.='<td><b>true</b></td>';
+		} else {
+		    $toprow.='<td><i>true</i></td>';
+		}
+	    } else {
+		$toprow.='<td>false</td>';
+	    }
+	    $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';
+	}
+	return '<blockquote><table border="1">'.
+	    '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
+	    '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.
+	    $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
+    } elsif ($response eq 'essay') {
 	if (! exists ($ENV{'form.'.$symb})) {
 	    my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade',
 						  $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
@@ -1421,7 +1485,7 @@ KEYWORDS
 
     my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
 
-    my ($partlist,$handgrade) = &response_type($url,$symb);
+    my ($partlist,$handgrade,$responseType) = &response_type($url,$symb);
 
     # Display student info
     $request->print(($counter == 0 ? '' : '<br />'));
@@ -1504,7 +1568,7 @@ KEYWORDS
 		$$fullname{$ENV{'form.'.$uname.':'.$udom.':submitted_by'}}.'</a>';
 	    $request->print($submitby);
 	} else {
-	    my ($string,$timestamp)= &get_last_submission (\%record);
+	    my ($string,$timestamp)= &get_last_submission(\%record);
 	    my $lastsubonly=''.
 		($$timestamp eq '' ? '' : '<b>Date Submitted:</b> '.
 		 $$timestamp)."</td></tr>\n";
@@ -1512,8 +1576,8 @@ KEYWORDS
 		$lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0]; 
 	    } else {
 		for my $part (sort keys(%$handgrade)) {
-		    my ($responsetype,$foo) = split(/:/,$$handgrade{$part});
 		    my ($partid,$respid) = split(/_/,$part);
+		    my $responsetype = $responseType->{$partid}->{$respid};
 		    if (!exists($record{'resource.'.$partid.'.'.$respid.'.submission'})) {
 			$lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part '.
 			    $partid.'</b> <font color="#999999">( ID '.$respid.
@@ -1521,7 +1585,7 @@ KEYWORDS
 			    '<font color="red">Nothing submitted - no attempts</font><br /><br />';
  		    } else {
 			foreach (@$string) {
-			    my ($partid,$respid) = /^resource\.(\w+)\.(\w+)\.submission/;
+			    my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/;
 			    if ($part eq ($partid.'_'.$respid)) {
 				my ($ressub,$subval) = split(/:/,$_,2);
                             # Similarity check
@@ -1541,6 +1605,7 @@ KEYWORDS
 					    &keywords_highlight($oessay).'</i></blockquote><hr />';
 				    }
 				}
+				my $order=&get_order($partid,$respid,$symb,$uname,$udom);
 				$lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part '.
 				    $partid.'</b> <font color="#999999">( ID '.$respid.
 				    ' )</font>&nbsp; &nbsp;'.
@@ -1551,11 +1616,11 @@ KEYWORDS
 				     '<font color="red" size="1">Like all files provided by users, '.
 				     'this file may contain virusses</font><br />':'').
 				     '<b>Submitted Answer: </b>'.
-				     &cleanRecord($subval,$responsetype,$symb).
+				     &cleanRecord($subval,$responsetype,$symb,$partid,$respid,\%record,$order).
 				     '<br /><br />'.$similar."\n"
 				     if ($ENV{'form.lastSub'} eq 'lastonly' || 
 					 ($ENV{'form.lastSub'} eq 'hdgrade' && 
-					  $$handgrade{$part} =~ /:yes$/));
+					  $$handgrade{$part} eq 'yes'));
 			    }
 			}
 		    }
@@ -1566,7 +1631,7 @@ KEYWORDS
 	}
     } elsif ($ENV{'form.lastSub'} eq 'datesub') {
 	my (undef,$responseType,undef,$parts) = &showResourceInfo($url);
-	$request->print(&displaySubByDates(\$symb,\%record,$parts,$responseType,$checkIcon));
+	$request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));
     } elsif ($ENV{'form.lastSub'} =~ /^(last|all)$/) {
 	$request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
 								 $ENV{'request.course.id'},
@@ -2172,7 +2237,7 @@ sub viewgrades {
 	my ($partid,$respid) = split (/_/,$_,2);
 	next if $seen{$partid};
 	$seen{$partid}++;
-	my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
+	my $handgrade=$$handgrade{$_};
 	my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
 	$weight{$partid} = $wgt eq '' ? '1' : $wgt;
 
@@ -2991,9 +3056,16 @@ sub displayPage {
 		} else {
 		    my %responseType = ();
 		    foreach my $partid (@{$parts}) {
-			$responseType{$partid} = $curRes->responseType($partid);
+			my @responseIds =$curRes->responseIds($partid);
+			my @responseType =$curRes->responseType($partid);
+			my %responseIds;
+			for (my $i=0;$i<=$#responseIds;$i++) {
+			    $responseIds{$responseIds[$i]}=$responseType[$i];
+			}
+			$responseType{$partid} = \%responseIds;
 		    }
-		    $studentTable.= &displaySubByDates(\$symbx,\%record,$parts,\%responseType,$checkIcon);
+		    $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);
+
 		}
 	    } elsif ($ENV{'form.lastSub'} eq 'all') {
 		my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');
@@ -3028,7 +3100,7 @@ sub displayPage {
 }
 
 sub displaySubByDates {
-    my ($symbx,$record,$parts,$responseType,$checkIcon) = @_;
+    my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;
     my $studentTable='<table border="0" width="100%"><tr><td bgcolor="#777777">'.
 	'<table border="0" width="100%"><tr bgcolor="#e6ffff">'.
 	'<td><b>Date/Time</b></td>'.
@@ -3036,33 +3108,62 @@ sub displaySubByDates {
 	'<td><b>Status&nbsp;</b></td></tr>';
     my ($version);
     my %mark;
+    my %orders;
     $mark{'correct_by_student'} = $checkIcon;
-    return '<br />&nbsp;<font color="red">Nothing submitted - no attempts</font><br />' 
-	if (!exists($$record{'1:timestamp'}));
+    if (!exists($$record{'1:timestamp'})) {
+	return '<br />&nbsp;<font color="red">Nothing submitted - no attempts</font><br />';
+    }
     for ($version=1;$version<=$$record{'version'};$version++) {
 	my $timestamp = scalar(localtime($$record{$version.':timestamp'}));
 	$studentTable.='<tr bgcolor="#ffffff" valign="top"><td>'.$timestamp.'</td>';
 	my @versionKeys = split(/\:/,$$record{$version.':keys'});
 	my @displaySub = ();
 	foreach my $partid (@{$parts}) {
-	    my @matchKey = grep /^resource\.$partid\..*?\.submission$/,@versionKeys;
+	    my @matchKey = sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys);
 #	    next if ($$record{"$version:resource.$partid.solved"} eq '');
-	    $displaySub[0].=(exists $$record{$version.':'.$matchKey[0]}) ? 
-		'<b>Part&nbsp;'.$partid.'&nbsp;'.
-		($$record{"$version:resource.$partid.tries"} eq '' ? 'Trial&nbsp;not&nbsp;counted' :
-		 'Trial&nbsp;'.$$record{"$version:resource.$partid.tries"}).'</b>&nbsp; '.
-		 &cleanRecord($$record{$version.':'.$matchKey[0]},$$responseType{$partid},$$symbx).'<br />' : '';
-	    $displaySub[1].=(exists $$record{"$version:resource.$partid.award"}) ?
-		'<b>Part&nbsp;'.$partid.'</b> &nbsp;'.
-		lc($$record{"$version:resource.$partid.award"}).' '.
-		$mark{$$record{"$version:resource.$partid.solved"}}.'<br />' : '';
-	    $displaySub[2].=(exists $$record{"$version:resource.$partid.regrader"}) ?
-		$$record{"$version:resource.$partid.regrader"}.' (<b>Part:</b> '.$partid.')' : '';
-	}
-	$displaySub[2].=(exists $$record{"$version:resource.regrader"}) ?
-	    $$record{"$version:resource.regrader"} : ''; # needed because old essay regrader has not parts info
-	$studentTable.='<td>'.$displaySub[0].'&nbsp;</td><td>'.$displaySub[1].
-	    ($displaySub[2] eq '' ? '' : 'Manually graded by '.$displaySub[2]).'&nbsp;</td></tr>';
+	    foreach my $matchKey (@matchKey) {
+		if (exists $$record{$version.':'.$matchKey}) {
+		    my ($responseId)=($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/);
+		    $displaySub[0].='<b>Part&nbsp;'.$partid.'&nbsp;';
+		    $displaySub[0].='<font color="#999999">(ID&nbsp;'.
+			$responseId.')</font>&nbsp;';
+		    if ($$record{"$version:resource.$partid.tries"} eq '') {
+			$displaySub[0].='Trial&nbsp;not&nbsp;counted';
+		    } else {
+			$displaySub[0].='Trial&nbsp;'.
+			    $$record{"$version:resource.$partid.tries"};
+		    }
+		    my $responseType=$responseType->{$partid}->{$responseId};
+		    if (!exists($orders{$partid})) { $orders{$partid}={}; }
+		    if (!exists($orders{$partid}->{$responseId})) {
+			$orders{$partid}->{$responseId}=
+			    &get_order($partid,$responseId,$symb,$uname,$udom);
+		    }
+		    $displaySub[0].='</b>&nbsp; '.
+			&cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:").'<br />';
+		}
+	    }
+	    if (exists $$record{"$version:resource.$partid.award"}) {
+		$displaySub[1].='<b>Part&nbsp;'.$partid.'</b> &nbsp;'.
+		    lc($$record{"$version:resource.$partid.award"}).' '.
+		    $mark{$$record{"$version:resource.$partid.solved"}}.
+		    '<br />';
+	    }
+	    if (exists $$record{"$version:resource.$partid.regrader"}) {
+		$displaySub[2].=$$record{"$version:resource.$partid.regrader"}.
+		    ' (<b>Part:</b> '.$partid.')';
+	    }
+	}
+	# needed because old essay regrader has not parts info
+	if (exists $$record{"$version:resource.regrader"}) {
+	    $displaySub[2].=$$record{"$version:resource.regrader"};
+	}
+	$studentTable.='<td>'.$displaySub[0].'&nbsp;</td><td>'.$displaySub[1];
+	if ($displaySub[2]) {
+	    $studentTable.='Manually graded by '.$displaySub[2];
+	}
+	$studentTable.='&nbsp;</td></tr>';
+    
     }
     $studentTable.='</table></td></tr></table>';
     return $studentTable;