--- loncom/homework/grades.pm	2004/12/15 19:09:35	1.236
+++ loncom/homework/grades.pm	2005/02/12 03:43:06	1.245
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.236 2004/12/15 19:09:35 albertel Exp $
+# $Id: grades.pm,v 1.245 2005/02/12 03:43:06 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -2655,15 +2655,17 @@ sub csvupload_javascript_reverse_associa
   function verify(vf) {
     var foundsomething=0;
     var founduname=0;
+    var foundID=0;
     var founddomain=0;
     for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');
-      if (i==0 && tw!=0) { founduname=1; }
-      if (i==1 && tw!=0) { founddomain=1; }
-      if (i!=0 && i!=1 && tw!=0) { foundsomething=1; }
+      if (i==0 && tw!=0) { foundID=1; }
+      if (i==1 && tw!=0) { founduname=1; }
+      if (i==2 && tw!=0) { founddomain=1; }
+      if (i!=0 && i!=1 && i!=2 && tw!=0) { foundsomething=1; }
     }
-    if (founduname==0 || founddomain==0) {
-      alert('You need to specify at both the username and domain');
+    if ((founduname==0 && foundID==0) || founddomain==0) {
+      alert('You need to specify the domain and either the username or ID');
       return;
     }
     if (foundsomething==0) {
@@ -2693,15 +2695,17 @@ sub csvupload_javascript_forward_associa
   function verify(vf) {
     var foundsomething=0;
     var founduname=0;
+    var foundID=0;
     var founddomain=0;
     for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');
-      if (tw==1) { founduname=1; }
-      if (tw==2) { founddomain=1; }
-      if (tw>2) { foundsomething=1; }
+      if (tw==1) { foundID=1; }
+      if (tw==2) { founduname=1; }
+      if (tw==3) { founddomain=1; }
+      if (tw>3) { foundsomething=1; }
     }
-    if (founduname==0 || founddomain==0) {
-      alert('You need to specify at both the username and domain');
+    if ((founduname==0 && foundID==0) || founddomain==0) {
+      alert('You need to specify the domain and either the username or ID');
       return;
     }
     if (foundsomething==0) {
@@ -2733,7 +2737,8 @@ sub csvuploadmap_header {
     }
 
     my ($result) = &showResourceInfo($url,$ENV{'form.probTitle'});
-
+    my $checked=(($ENV{'form.noFirstLine'})?' checked="checked"':'');
+    my $ignore=&mt('Ignore First Line');
     $request->print(<<ENDPICK);
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 <h3><font color="#339933">Uploading Class Grades</font></h3>
@@ -2744,6 +2749,7 @@ Total number of records found in file: $
 Enter as many fields as you can. The system will inform you and bring you back
 to this page if the data selected is insufficient to run your class.<hr />
 <input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
+<label><input type="checkbox" name="noFirstLine" $checked />$ignore</label>
 <input type="hidden" name="associate"  value="" />
 <input type="hidden" name="phase"      value="three" />
 <input type="hidden" name="datatoken"  value="$datatoken" />
@@ -2768,13 +2774,18 @@ ENDPICK
 sub csvupload_fields {
     my ($url,$symb) = @_;
     my (@parts) = &getpartlist($url,$symb);
-    my @fields=(['username','Student Username'],['domain','Student Domain']);
+    my @fields=(['ID','Student ID'],
+		['username','Student Username'],
+		['domain','Student Domain']);
     foreach my $part (sort(@parts)) {
 	my @datum;
 	my $display=&Apache::lonnet::metadata($url,$part.'.display');
 	my $name=$part;
 	if  (!$display) { $display = $name; }
 	@datum=($name,$display);
+	if ($name=~/^stores_(.*)_awarded/) {
+	    push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);
+	}
 	push(@fields,\@datum);
     }
     return (@fields);
@@ -2815,6 +2826,7 @@ CSVFORMJS
 	'.</b></td></tr>'."\n";
     $result.='<tr bgcolor=#ffffe6><td>'."\n";
     my $upfile_select=&Apache::loncommon::upfile_select_html();
+    my $ignore=&mt('Ignore First Line');
     $result.=<<ENDUPFORM;
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 <input type="hidden" name="symb" value="$symb" />
@@ -2824,7 +2836,7 @@ CSVFORMJS
 <input type="hidden" name="saveState"  value="$ENV{'form.saveState'}" />
 $upfile_select
 <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scores" />
-
+<label><input type="checkbox" name="noFirstLine" />$ignore</lable>
 </form>
 ENDUPFORM
     $result.='</td></tr></table>'."\n";
@@ -2847,6 +2859,7 @@ sub csvuploadmap {
 	&Apache::loncommon::load_tmp_file($request);
     }
     my @records=&Apache::loncommon::upfile_record_sep();
+    if ($ENV{'form.noFirstLine'}) { shift(@records); }
     &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);
     my ($i,$keyfields);
     if (@records) {
@@ -2878,6 +2891,7 @@ sub csvuploadassign {
     if (!$symb) {return '';}
     &Apache::loncommon::load_tmp_file($request);
     my @gradedata = &Apache::loncommon::upfile_record_sep();
+    if ($ENV{'form.noFirstLine'}) { shift(@gradedata); }
     my @keyfields = split(/\,/,$ENV{'form.keyfields'});
     my %fields=();
     for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) {
@@ -2899,10 +2913,15 @@ sub csvuploadassign {
     my $countdone=0;
     foreach my $grade (@gradedata) {
 	my %entries=&Apache::loncommon::record_sep($grade);
-	my $username=$entries{$fields{'username'}};
-	$username=~s/\s//g;
 	my $domain=$entries{$fields{'domain'}};
 	$domain=~s/\s//g;
+	my $username=$entries{$fields{'username'}};
+	$username=~s/\s//g;
+	if (!$username) {
+	    my $id=$entries{$fields{'ID'}};
+	    my %ids=&Apache::lonnet::idget($domain,$id);
+	    $username=$ids{$id};
+	}
 	if (!exists($$classlist{"$username:$domain"})) {
 	    push(@skipped,"$username:$domain");
 	    next;
@@ -2912,16 +2931,33 @@ sub csvuploadassign {
 	    push(@notallowed,"$username:$domain");
 	    next;
 	}
+	my %points;
 	my %grades;
 	foreach my $dest (keys(%fields)) {
-	    if ($dest eq 'username' || $dest eq 'domain') { next; }
-	    if ($entries{$fields{$dest}} eq '') { next; }
-	    my $store_key=$dest;
-	    $store_key=~s/^stores/resource/;
-	    $store_key=~s/_/\./g;
-	    $grades{$store_key}=$entries{$fields{$dest}};
+	    if ($dest eq 'ID' || $dest eq 'username' ||
+		$dest eq 'domain') { next; }
+	    if ($entries{$fields{$dest}} =~ /^\s*$/) { next; }
+	    if ($dest=~/stores_(.*)_points/) {
+		my $part=$1;
+		my $wgt =&Apache::lonnet::EXT('resource.'.$part.'.weight',
+					      $symb,$domain,$username);
+		my $pcr=$entries{$fields{$dest}} / $wgt;
+		my $award='correct_by_override';
+		$grades{"resource.$part.awarded"}=$pcr;
+		$grades{"resource.$part.solved"}=$award;
+		$points{$part}=1;
+	    } else {
+		if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }
+		if ($dest=~/stores_(.*)_solved/)  { if ($points{$1}) {next;} }
+		my $store_key=$dest;
+		$store_key=~s/^stores/resource/;
+		$store_key=~s/_/\./g;
+		$grades{$store_key}=$entries{$fields{$dest}};
+	    }
 	}
+	if (! %grades) { push(@skipped,"$username:$domain no data to store"); }
 	$grades{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}";
+#	&Apache::lonnet::logthis(" storing ".(join('-',%grades)));
 	&Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'},
 				$domain,$username);
 	$request->print('.');
@@ -3060,7 +3096,8 @@ sub getSymbMap {
     my $minder = 0;
 
     # Gather every sequence that has problems.
-    my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); }, 1);
+    my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },
+					       1,0,1);
     for my $sequence ($navmap->getById('0.0'), @sequences) {
 	if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {
 	    my $title = $minder.'.'.$sequence->compTitle();
@@ -3796,25 +3833,49 @@ sub scantron_parse_scanline {
 	my $currentquest=substr($questions,0,$$scantron_config{'Qlength'});
 	substr($questions,0,$$scantron_config{'Qlength'})='';
 	if (length($currentquest) < $$scantron_config{'Qlength'}) { next; }
-	my @array=split($$scantron_config{'Qon'},$currentquest,-1);
-	if (length($array[0]) eq $$scantron_config{'Qlength'}) {
-	    $record{"scantron.$questnum.answer"}='';
-	    if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {
-		push(@{$record{"scantron.missingerror"}},$questnum);
- 	    }
+	if ($$scantron_config{'Qon'} eq 'letter') {
+	    if (!$currentquest || $currentquest eq $$scantron_config{'Qoff'} ||
+		$currentquest !~ /^[A-Z]$/) {
+		$record{"scantron.$questnum.answer"}='';
+		if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {
+		    push(@{$record{"scantron.missingerror"}},$questnum);
+		}
+	    } else {
+		$record{"scantron.$questnum.answer"}=$currentquest;
+	    }
+	} elsif ($$scantron_config{'Qon'} eq 'number') {
+	    if (!$currentquest || $currentquest eq $$scantron_config{'Qoff'} ||
+		$currentquest !~ /^\d$/) {
+		$record{"scantron.$questnum.answer"}='';
+		if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {
+		    push(@{$record{"scantron.missingerror"}},$questnum);
+		}
+	    } else {
+		$record{"scantron.$questnum.answer"}=
+		    $alphabet[$currentquest-1];
+	    }
 	} else {
-	    $record{"scantron.$questnum.answer"}=$alphabet[length($array[0])];
+	    my @array=split($$scantron_config{'Qon'},$currentquest,-1);
+	    if (length($array[0]) eq $$scantron_config{'Qlength'}) {
+		$record{"scantron.$questnum.answer"}='';
+		if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {
+		    push(@{$record{"scantron.missingerror"}},$questnum);
+		}
+	    } else {
+		$record{"scantron.$questnum.answer"}=
+		    $alphabet[length($array[0])];
+	    }
+	    if (scalar(@array) gt 2) {
+		push(@{$record{'scantron.doubleerror'}},$questnum);
+		my @ans=@array;
+		my $i=length($ans[0]);shift(@ans);
+		while ($#ans) {
+		    $i+=length($ans[0])+1;
+		    $record{"scantron.$questnum.answer"}.=$alphabet[$i];
+		    shift(@ans);
+		}
+	    }
 	}
- 	if (scalar(@array) gt 2) {
- 	    push(@{$record{'scantron.doubleerror'}},$questnum);
- 	    my @ans=@array;
- 	    my $i=length($ans[0]);shift(@ans);
-	    while ($#ans) {
- 		$i+=length($ans[0])+1;
- 		$record{"scantron.$questnum.answer"}.=$alphabet[$i];
- 		shift(@ans);
- 	    }
- 	}
     }
     $record{'scantron.maxquest'}=$questnum;
     return \%record;
@@ -3979,14 +4040,28 @@ sub scantron_do_warning {
     if (!$symb) {return '';}
     my $default_form_data=&defaultFormData($symb,$url);
     $r->print(&scantron_form_start().$default_form_data);
-    my $warning=&scantron_warning_screen('Validate Records');
-    $r->print(<<STUFF);
+    if ( $ENV{'form.selectpage'} eq '' ||
+	 $ENV{'form.scantron_selectfile'} eq '' ||
+	 $ENV{'form.scantron_format'} eq '' ) {
+	$r->print("<p>You have forgetten to specify some information. Please go Back and try again.</p>");
+	if ( $ENV{'form.selectpage'} eq '') {
+	    $r->print('<p><font color="red">You have not selected a Sequence to grade</font></p>');
+	} 
+	if ( $ENV{'form.scantron_selectfile'} eq '') {
+	    $r->print('<p><font color="red">You have not selected a file that contains the student\'s response data.</font></p>');
+	} 
+	if ( $ENV{'form.scantron_format'} eq '') {
+	    $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');
+	$r->print(<<STUFF);
 $warning
 <input type="submit" name="submit" value="Validate Records" />
 <input type="hidden" name="command" value="scantron_validate" />
-</form>
 STUFF
-    $r->print("<br />".&show_grading_menu_form($symb,$url)."</body></html>");
+    }
+    $r->print("</form><br />".&show_grading_menu_form($symb,$url)."</body></html>");
     return '';
 }
 
@@ -4287,6 +4362,12 @@ sub scantron_get_correction {
 	$r->print(" in scanline $i <pre>".
 		  $line."</pre> \n");
     }
+    my $message="<p>The ID on the form is  <tt>".
+	$$scan_record{'scantron.ID'}."</tt><br />\n".
+	"The name on the paper is ".
+	$$scan_record{'scantron.LastName'}.",".
+	$$scan_record{'scantron.FirstName'}."</p>";
+
     $r->print('<input type="hidden" name="scantron_corrections" value="'.$error.'" />'."\n");
     $r->print('<input type="hidden" name="scantron_line" value="'.$i.'" />'."\n");
     if ($error =~ /ID$/) {
@@ -4295,11 +4376,7 @@ sub scantron_get_correction {
 	} elsif ($error eq 'duplicateID') {
 	    $r->print("The encoded ID has also been used by a previous paper $arg</p>\n");
 	}
-	$r->print("<p>The ID on the form is  <tt>".
-		  $$scan_record{'scantron.ID'}."</tt><br />\n");
-	$r->print("The name on the paper is ".
-		  $$scan_record{'scantron.LastName'}.",".
-		  $$scan_record{'scantron.FirstName'}."</p>");
+	$r->print($message);
 	$r->print("<p>How should I handle this? <br /> \n");
 	$r->print("\n<ul><li> ");
 	#FIXME it would be nice if this sent back the user ID and
@@ -4319,11 +4396,7 @@ sub scantron_get_correction {
 	}
 	$r->print("<p>The CODE on the form is  <tt>'".
 		  $$scan_record{'scantron.CODE'}."'</tt><br />\n");
-	$r->print("<p>The ID on the form is  <tt>".
-		  $$scan_record{'scantron.ID'}."</tt><br />\n");
-	$r->print("The name on the paper is ".
-		  $$scan_record{'scantron.LastName'}.",".
-		  $$scan_record{'scantron.FirstName'}."</p>");
+	$r->print($message);
 	$r->print("<p>How should I handle this? <br /> \n");
 	$r->print("\n<br /> ");
 	my $i=0;
@@ -4366,6 +4439,7 @@ ENDSCRIPT
 	$r->print("<p>There have been multiple bubbles scanned for a some question(s)</p>\n");
 	$r->print('<input type="hidden" name="scantron_questions" value="'.
 		  join(',',@{$arg}).'" />');
+	$r->print($message);
 	$r->print("<p>Please indicate which bubble should be used for grading</p>");
 	foreach my $question (@{$arg}) {
 	    my $selected=$$scan_record{"scantron.$question.answer"};
@@ -4373,6 +4447,7 @@ ENDSCRIPT
 	}
     } elsif ($error eq 'missingbubble') {
 	$r->print("<p>There have been <b>no</b> bubbles scanned for some question(s)</p>\n");
+	$r->print($message);
 	$r->print("<p>Please indicate which bubble should be used for grading</p>");
 	$r->print("Some questions have no scanned bubbles\n");
 	$r->print('<input type="hidden" name="scantron_questions" value="'.
@@ -4810,7 +4885,6 @@ DOWNLOAD
 #
 #-------------------------------------------------------------------
 
-
 #-------------------------- Menu interface -------------------------
 #
 #--- Show a Grading Menu button - Calls the next routine ---
@@ -4863,6 +4937,7 @@ sub gradingmenu {
 	    if (!checkReceiptNo(formname,'notOK')) { return false;}
 	    formname.submit();
 	}
+	if (val < 7) formname.submit();
     }
 
     function checkReceiptNo(formname,nospace) {
@@ -4915,7 +4990,7 @@ GRADINGMENUJS
 		($saveSec eq $_ ? 'selected="on"':'').'>'.$_.'</option>'."\n";
 	}
     }
-    $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</select> &nbsp; ';
+    $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</option></select> &nbsp; ';
 
     $result.=&mt('Student Status').':</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef);
 
@@ -4967,6 +5042,9 @@ GRADINGMENUJS
 	    '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')">'.
 	    '</td></tr>'."\n";
     } 
+    $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.
+	'<input type="button" onClick="javascript:this.form.action=\'/adm/helper/resettimes.helper\';this.form.submit();'.
+	'" value="'.&mt('Manage').'" /> access times.</td></tr>'."\n";
 
     $result.='</form></td></tr></table>'."\n".
 	'</td></tr></table>'."\n".