--- loncom/homework/grades.pm 2003/02/28 21:06:28 1.70
+++ loncom/homework/grades.pm 2003/10/16 03:51:52 1.130.2.1.2.11
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.70 2003/02/28 21:06:28 ng Exp $
+# $Id: grades.pm,v 1.130.2.1.2.11 2003/10/16 03:51:52 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -32,7 +32,8 @@
# Year 2002
# June-August H.K. Ng
# Year 2003
-# February H.K. Ng
+# February, March H.K. Ng
+# July, H. K. Ng
#
package Apache::grades;
@@ -41,11 +42,16 @@ use Apache::style;
use Apache::lonxml;
use Apache::lonnet;
use Apache::loncommon;
+use Apache::lonhtmlcommon;
use Apache::lonnavmaps;
use Apache::lonhomework;
use Apache::loncoursedata;
use Apache::lonmsg qw(:user_normal_msg);
use Apache::Constants qw(:common);
+use String::Similarity;
+
+my %oldessays=();
+my %perm=();
# ----- These first few routines are general use routines.----
#
@@ -90,10 +96,23 @@ sub get_fullname {
return $fullname;
}
+#--- Format fullname, username:domain if different for display
+#--- Use anywhere where the student names are listed
+sub nameUserString {
+ my ($type,$fullname,$uname,$udom) = @_;
+ if ($type eq 'header') {
+ return ' Fullname (Username) ';
+ } else {
+ return ' '.$fullname.' ('.$uname.
+ ($ENV{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')';
+ }
+}
+
#--- Get the partlist and the response type for a given problem. ---
#--- Indicate if a response type is coded handgraded or not. ---
sub response_type {
- my ($url) = shift;
+ my ($url,$symb) = shift;
+ $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);
@@ -101,7 +120,9 @@ sub response_type {
if (/^\w+response_\w+.*/) {
my ($responsetype,$part) = split(/_/,$_,2);
my ($partid,$respid) = split(/_/,$part);
- $handgrade{$part} = $responsetype.':'.($allkeys =~ /parameter_$part\_handgrade/ ? 'yes' : 'no');
+ $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');
next if ($seen{$partid} > 0);
$seen{$partid}++;
push @partlist,$partid;
@@ -110,10 +131,105 @@ sub response_type {
return \@partlist,\%handgrade;
}
+#--- Show resource title
+#--- and parts and response type
+sub showResourceInfo {
+ my ($url,$probTitle) = @_;
+ my $result ='
'.
+ '
Current Resource: '.$probTitle.'
'."\n";
+ my ($partlist,$handgrade) = &response_type($url);
+ my %resptype = ();
+ my $hdgrade='no';
+ for (sort keys(%$handgrade)) {
+ my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
+ my $partID = (split(/_/))[0];
+ $resptype{$partID} = $responsetype;
+ $hdgrade = $handgrade if ($handgrade eq 'yes');
+ $result.='
Part '.$partID.'
'.
+ '
Type: '.$responsetype.'
';
+# '
Handgrade: '.$handgrade.'
';
+ }
+ $result.='
'."\n";
+ return $result,\%resptype,$hdgrade,$partlist,$handgrade;
+}
+
+#--- Clean response type for display
+#--- Currently filters option response type 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.'';
+ push @ans,$ans;
+ }
+ my $grayFont = '';
+ return '
'.
+ '
Answer
'.
+ (join '
',@ans).'
'.
+ '
'.$grayFont.'Option ID
'.$grayFont.
+ (join '
'.$grayFont,@IDs).'
'.
+ '
';
+ }
+ if ($response eq 'essay') {
+ if (! exists ($ENV{'form.'.$symb})) {
+ my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade',
+ $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
+ $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
+
+ my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};
+ $ENV{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
+ $ENV{'form.kwclr'} = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
+ $ENV{'form.kwsize'} = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
+ $ENV{'form.kwstyle'} = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
+ $ENV{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.
+ }
+ return '
'.&keywords_highlight($answer).'
';
+ }
+ return $answer;
+}
+
+#-- A couple of common js functions
+sub commonJSfunctions {
+ my $request = shift;
+ $request->print(<
+ function radioSelection(radioButton) {
+ var selection=null;
+ if (radioButton.length > 1) {
+ for (var i=0; i 1) {
+ for (var i=0; i
+COMMONJSFUNCTIONS
+}
+
#--- Dumps the class list with usernames,list of sections,
#--- section, ids and fullnames for each user.
sub getclasslist {
- my ($getsec,$hideexpired) = @_;
+ my ($getsec,$filterlist) = @_;
+ $getsec = $getsec eq '' ? 'all' : $getsec;
my $classlist=&Apache::loncoursedata::get_classlist();
# Bail out if we were unable to get the classlist
return if (! defined($classlist));
@@ -124,59 +240,68 @@ sub getclasslist {
# the following undefs are for 'domain', and 'username' respectively.
my (undef,undef,$end,$start,$id,$section,$fullname,$status)=
@{$classlist->{$_}};
- # still a student?
- if (($hideexpired) && ($status ne 'Active')) {
- delete ($classlist->{$_});
- next;
- }
+ # filter students according to status selected
+ if ($filterlist && $ENV{'form.Status'} ne 'Any') {
+ if ($ENV{'form.Status'} ne $status) {
+ delete ($classlist->{$_});
+ next;
+ }
+ }
$section = ($section ne '' ? $section : 'no');
- if ($getsec eq 'all' || $getsec eq $section) {
- $sections{$section}++;
- $fullnames{$_}=$fullname;
- } else {
- delete($classlist->{$_});
- }
+ if (&canview($section)) {
+ if ($getsec eq 'all' || $getsec eq $section) {
+ $sections{$section}++;
+ $fullnames{$_}=$fullname;
+ } else {
+ delete($classlist->{$_});
+ }
+ } else {
+ delete($classlist->{$_});
+ }
}
my %seen = ();
my @sections = sort(keys(%sections));
return ($classlist,\@sections,\%fullnames);
}
-#find user domain
-sub finduser {
- my ($name) = @_;
- my $domain = '';
- if ( $Apache::grades::viewgrades eq 'F' ) {
- my %classlist=&Apache::lonnet::dump('classlist',
- $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
- $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
- my (@fields) = grep /^$name:/, keys %classlist;
- ($name, $domain) = split(/:/,$fields[0]);
- return ($name,$domain);
- } else {
- return ($ENV{'user.name'},$ENV{'user.domain'});
+sub canmodify {
+ my ($sec)=@_;
+ if ($perm{'mgr'}) {
+ if (!defined($perm{'mgr_section'})) {
+ # can modify whole class
+ return 1;
+ } else {
+ if ($sec eq $perm{'mgr_section'}) {
+ #can modify the requested section
+ return 1;
+ } else {
+ # can't modify the request section
+ return 0;
+ }
+ }
}
+ #can't modify
+ return 0;
}
-#--- Prompts a user to enter a username.
-sub moreinfo {
- my ($request,$reason) = @_;
- $request->print("Unable to process request: $reason");
- if ( $Apache::grades::viewgrades eq 'F' ) {
- $request->print('');
+sub canview {
+ my ($sec)=@_;
+ if ($perm{'vgr'}) {
+ if (!defined($perm{'vgr_section'})) {
+ # can modify whole class
+ return 1;
+ } else {
+ if ($sec eq $perm{'vgr_section'}) {
+ #can modify the requested section
+ return 1;
+ } else {
+ # can't modify the request section
+ return 0;
+ }
+ }
}
- return '';
+ #can't modify
+ return 0;
}
#--- Retrieve the grade status of a student for all the parts
@@ -185,7 +310,7 @@ sub student_gradeStatus {
my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
my %partstatus = ();
foreach (@$partlist) {
- my ($status,$foo) = split(/_/,$record{"resource.$_.solved"},2);
+ my ($status,undef) = split(/_/,$record{"resource.$_.solved"},2);
$status = 'nothing' if ($status eq '');
$partstatus{$_} = $status;
my $subkey = "resource.$_.submitted_by";
@@ -209,6 +334,9 @@ sub jscriptNform {
$jscript.= ''."\n".
- 'Resource: '.$ENV{'form.url'}.'
'."\n";
+ 'Resource: '.$ENV{'form.probTitle'}.'
'."\n";
my ($string,$contents,$matches) = ('','',0);
my (undef,undef,$fullname) = &getclasslist('all','0');
@@ -272,10 +444,6 @@ sub verifyreceipt {
return $string.&show_grading_menu_form($symb,$url);
}
-#
-# Pick student and page/sequence for manual grading
-
-
#--- This is called by a number of programs.
#--- Called from the Grading Menu - View/Grade an individual student
#--- Also called directly when one clicks on the subm button
@@ -283,140 +451,178 @@ sub verifyreceipt {
sub listStudents {
my ($request) = shift;
- my ($symb,$url) = &get_symb_and_url();
+ my ($symb,$url) = &get_symb_and_url($request);
my $cdom = $ENV{"course.$ENV{'request.course.id'}.domain"};
my $cnum = $ENV{"course.$ENV{'request.course.id'}.num"};
my $getsec = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
my $submitonly= $ENV{'form.submitonly'} eq '' ? 'all' : $ENV{'form.submitonly'};
- my $result;
- my ($partlist,$handgrade) = &response_type($url);
- for (sort keys(%$handgrade)) {
- my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
- $ENV{'form.handgrade'} = 'yes' if ($handgrade eq 'yes');
- $result.='
'."\n";
+
+ $request->print($result);
- my $navmap = Apache::lonnavmaps::navmap-> new(
- $ENV{'request.course.fn'}.'.db',
+ my $navmap = Apache::lonnavmaps::navmap-> new($ENV{'request.course.fn'}.'.db',
$ENV{'request.course.fn'}.'_parms.db',1, 1);
my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'});
my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
@@ -2470,169 +3016,1152 @@ sub displayPage {
my $iterator = $navmap->getIterator($map->map_start(),
$map->map_finish());
- my $depth = 1;
+ my $studentTable='
'.
+ '
'.
+ '
Prob.
'.
+ '
Title
'.
+ '
Previous Score
'.
+ '
New Score
';
+
$iterator->next(); # skip the first BEGIN_MAP
my $curRes = $iterator->next(); # for "current resource"
- my %symbx = ();
- my @titles = ();
- my %parts = ();
- my $ctr=0;
- my $minder=0;
- while ($depth > 0 && $ctr < 100) { # ctr, just in case it never gets out of loop
+ my ($depth,$question,$changeflag)= (1,1,0);
+ while ($depth > 0) {
if($curRes == $iterator->BEGIN_MAP) { $depth++; }
- if($curRes == $iterator->END_MAP) { $depth++; }
+ if($curRes == $iterator->END_MAP) { $depth--; }
if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
my $parts = $curRes->parts();
my $title = $curRes->compTitle();
- push @titles,$minder.'.'.$title; # minder, just in case two titles are identical
- if (scalar(@{$parts}) > 1) { shift @{$parts}; }
- $parts{$minder.'.'.$title} = join '::',@{$parts};
- $symbx{$minder.'.'.$title} = $curRes->symb();
- $minder++;
+ my $symbx = $curRes->symb();
+ $studentTable.='
';
- my $question=1;
- foreach (@titles) {
+#-------- end of section for handling grading by page/sequence ---------
+#
+#-------------------------------------------------------------------
+
+#--------------------Scantron Grading-----------------------------------
+#
+#------ start of section for handling grading by page/sequence ---------
+
+sub defaultFormData {
+ my ($symb,$url)=@_;
+ return '
+ '."\n".
+ ''."\n".
+ ''."\n".
+ ''."\n";
+}
+
+sub getSequenceDropDown {
+ my ($request,$symb)=@_;
+ my $result='
';
- $studentTable.='
';
+sub scantron_filter {
+ my ($curres)=@_;
+ if (ref($curres) && $curres->is_problem() && !$curres->randomout) {
+ return 1;
+ }
+ return 0;
+}
- $request->print($studentTable);
+sub scantron_process_corrections {
+ my ($r) = @_;
+ my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'});
+ my ($scanlines,$scan_data)=&scantron_getfile();
+ my $classlist=&Apache::loncoursedata::get_classlist();
+ my $which=$ENV{'form.scantron_line'};
+ my $line=&scantron_get_line($scanlines,$which);
+ my ($skip,$err,$errmsg);
+ if ($ENV{'form.scantron_skip_record'}) {
+ $skip=1;
+ } elsif ($ENV{'form.scantron_corrections'} =~ /^(duplicate|incorrect)ID$/) {
+ my $newstudent=$ENV{'form.scantron_username'}.':'.
+ $ENV{'form.scantron_domain'};
+ my $newid=$classlist->{$newstudent}->[&Apache::loncoursedata::CL_ID];
+ ($line,$err,$errmsg)=
+ &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,$which,
+ 'ID',{'newid'=>$newid,
+ 'username'=>$ENV{'form.scantron_username'},
+ 'domain'=>$ENV{'form.scantron_domain'}});
+ } elsif ($ENV{'form.scantron_corrections'} =~ /^(missing|double)bubble$/) {
+ foreach my $question (split(',',$ENV{'form.scantron_questions'})) {
+ ($line,$err,$errmsg)=
+ &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,
+ $which,'answer',
+ { 'question'=>$question,
+ 'response'=>$ENV{"form.scantron_correct_Q_$question"}});
+ if ($err) { last; }
+ }
+ }
+ if ($err) {
+ $r->print("Unable to accept last correction, an error occurred :$errmsg:");
+ } else {
+ &scantron_put_line($scanlines,$which,$line,$skip);
+ &scantron_putfile($scanlines,$scan_data);
+ }
+}
+
+
+sub scantron_validate_file {
+ my ($r) = @_;
+ my ($symb,$url)=&get_symb_and_url($r);
+ if (!$symb) {return '';}
+ my $default_form_data=&defaultFormData($symb,$url);
+
+ if ($ENV{'form.scantron_corrections'}) {
+ &scantron_process_corrections($r);
+ }
+ #get the student pick code ready
+ $r->print(&Apache::loncommon::studentbrowser_javascript());
+ my $result= <
+
+
+
+
+ $default_form_data
+SCANTRONFORM
+ $r->print($result);
+
+ my @validate_phases=( 'ID',
+ 'CODE',
+ 'doublebubble',
+ 'missingbubbles');
+ if (!$ENV{'form.validatepass'}) {
+ $ENV{'form.valiadatepass'} = 0;
+ }
+ my $currentphase=$ENV{'form.valiadatepass'};
+ if ($ENV{'form.scantron_selectfile'}=~m-^/-) {
+ #first pass copy file to classdir
+
+ }
+ my $stop=0;
+ while (!$stop && $currentphase < scalar(@validate_phases)) {
+ $r->print("
Validating ".$validate_phases[$currentphase]."
");
+ $r->rflush();
+ my $which="scantron_validate_".$validate_phases[$currentphase];
+ {
+ no strict 'refs';
+ ($stop,$currentphase)=&$which($r,$currentphase);
+ }
+ }
+ if (!$stop) {
+ $r->print("Validation process complete. ");
+ $r->print('');
+ $r->print('');
+ } else {
+ $r->print('');
+ $r->print("");
+ }
+ if ($stop) {
+ $r->print('');
+ $r->print(' using corrected info ');
+ $r->print("");
+ $r->print(" this scanline saving it for later.");
+ }
+ $r->print(" ".&show_grading_menu_form($symb,$url).
+ "");
return '';
}
-sub gradeBox {
- my ($symb,$uname,$udom,$counter,$partid,$record) = @_;
- my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);
- my $wgtmsg = ($wgt > 0 ? '(problem weight)' :
- 'problem weight assigned by computer');
- $wgt = ($wgt > 0 ? $wgt : '1');
- my $score = ($$record{'resource.'.$partid.'.awarded'} eq '' ?
- '' : $$record{'resource.'.$partid.'.awarded'}*$wgt);
- my $result='';
- $result.='
Part '.$partid.' Points:
';
+sub scantron_getfile {
+ #FIXME really would prefer a scantron directory but tokenwrapper
+ # doesn't allow access to subdirs of userfiles
+ my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
+ my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
+ my $lines;
+ $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.
+ 'scantron_orig_'.$ENV{'form.scantron_selectfile'});
+ if ($lines eq '-1') {
+ #FIXME need to actually replicate file to course space
+ #FIXME when replicating strip CRLF to LF or CR to LF
+ }
+ my %scanlines;
+ $scanlines{'orig'}=[(split("\n",$lines,-1))];
+ my $temp=$scanlines{'orig'};
+ $scanlines{'count'}=$#$temp;
+
+ $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.
+ 'scantron_corrected_'.$ENV{'form.scantron_selectfile'});
+ if ($lines eq '-1') {
+ $scanlines{'corrected'}=[];
+ } else {
+ $scanlines{'corrected'}=[(split("\n",$lines,-1))];
+ }
+ $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.
+ 'scantron_skipped_'.$ENV{'form.scantron_selectfile'});
+ if ($lines eq '-1') {
+ $scanlines{'skipped'}=[];
+ } else {
+ $scanlines{'skipped'}=[(split("\n",$lines,-1))];
+ }
+ my @tmp=&Apache::lonnet::dump('scantrondata',$cdom,$cname);
+ if ($tmp[0] =~ /^(error:|no_such_host)/) { @tmp=(); }
+ my %scan_data = @tmp;
+ return (\%scanlines,\%scan_data);
+}
+
+sub lonnet_putfile {
+ my ($contents,$filename)=@_;
+ my $docuname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
+ my $docudom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
+ my $docuhome=$ENV{'course.'.$ENV{'request.course.id'}.'.home'};
+ $ENV{'form.sillywaytopassafilearound'}=$contents;
+ &Apache::lonnet::finishuserfileupload($docuname,$docudom,$docuhome,'sillywaytopassafilearound',$filename);
- my $ctr = 0;
- $result.='
'; # display radio buttons in a nice table 10 across
- while ($ctr<=$wgt) {
- $result.= '
'.$ctr."
\n";
- $result.=(($ctr+1)%10 == 0 ? '
' : '');
- $ctr++;
+}
+
+sub scantron_putfile {
+ my ($scanlines,$scan_data) = @_;
+ #FIXME really would prefer a scantron directory but tokenwrapper
+ # doesn't allow access to subdirs of userfiles
+ my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
+ my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
+ my $prefix='scantron_';
+# no need to update orig, shouldn't change
+# &lonnet_putfile(join("\n",@{$scanlines->{'orig'}}),$prefix.'orig_'.
+# $ENV{'form.scantron_selectfile'});
+ &lonnet_putfile(join("\n",@{$scanlines->{'corrected'}}),
+ $prefix.'corrected_'.
+ $ENV{'form.scantron_selectfile'});
+ &lonnet_putfile(join("\n",@{$scanlines->{'skipped'}}),
+ $prefix.'skipped_'.
+ $ENV{'form.scantron_selectfile'});
+ &Apache::lonnet::put('scantrondata',$scan_data,$cdom,$cname);
+}
+
+sub scantron_get_line {
+ my ($scanlines,$i)=@_;
+ if ($scanlines->{'skipped'}[$i]) {return undef;}
+ if ($scanlines->{'corrected'}[$i]) {return $scanlines->{'corrected'}[$i];}
+ return $scanlines->{'orig'}[$i];
+}
+
+sub scantron_put_line {
+ my ($scanlines,$i,$newline,$skip)=@_;
+ if ($skip) {
+ $scanlines->{'skipped'}[$i]=$newline;
+ return;
}
- $result.='
';
- $result.='
or
';
- $result.='
'."\n";
- $result.='
/'.$wgt.' '.$wgtmsg.'
';
+ $scanlines->{'corrected'}[$i]=$newline;
+}
- $result.=''."\n";
- if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {
- $result.=''.
- '';
+sub scantron_validate_ID {
+ my ($r,$currentphase) = @_;
+
+ #get student info
+ my $classlist=&Apache::loncoursedata::get_classlist();
+ my %idmap=&username_to_idmap($classlist);
+
+ #get scantron line setup
+ my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'});
+ my ($scanlines,$scan_data)=&scantron_getfile();
+
+ my %found=('ids'=>{},'usernames'=>{});
+ for (my $i=0;$i<=$scanlines->{'count'};$i++) {
+ my $line=&scantron_get_line($scanlines,$i);
+ if ($line=~/^[\s\cz]*$/) { next; }
+ my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
+ $scan_data);
+ my $id=$$scan_record{'scantron.ID'};
+ my $found;
+ foreach my $checkid (keys(%idmap)) {
+ if (lc($checkid) eq lc($id)) { $found=$checkid;last; }
+ }
+ if ($found) {
+ my $username=$idmap{$found};
+ if ($found{'ids'}{$found}) {
+ &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
+ $line,'duplicateID',$found);
+ return(1);
+ } elsif ($found{'usernames'}{$username}) {
+ &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
+ $line,'duplicateID',$username);
+ return(1);
+ }
+ #FIXME store away line we prviously saw the ID on to use above
+ $found{'ids'}{$found}++;
+ $found{'usernames'}{$username}++;
+ } else {
+ if ($id =~ /^\s*$/) {
+ my $username=&scan_data($scan_data,"$i.user");
+ if (defined($username) && $found{'usernames'}{$username}) {
+ &scantron_get_correction($r,$i,$scan_record,
+ \%scantron_config,
+ $line,'duplicateID',$username);
+ return(1);
+ } elsif (!defined($username)) {
+ &scantron_get_correction($r,$i,$scan_record,
+ \%scantron_config,
+ $line,'incorrectID');
+ return(1);
+ }
+ $found{'usernames'}{$username}++;
+ } else {
+ &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
+ $line,'incorrectID');
+ return(1);
+ }
+ }
+ }
+
+ return (0,$currentphase+1);
+}
+
+sub scantron_get_correction {
+ my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_;
+
+#FIXME in the case of a duplicated ID the previous line, probaly need
+#to show both the current line and the previous one and allow skipping
+#the previous one or the current one
+
+ $r->print("
An error was detected ($error) ");
+ if ( defined($$scan_record{'scantron.PaperID'}) ) {
+ $r->print(" for PaperID ".
+ $$scan_record{'scantron.PaperID'}." \n");
} else {
- $result.=''.
- '';
+ $r->print(" in scanline $i
'."\n";
+ $r->print(''."\n");
+ $r->print(''."\n");
+ if ($error =~ /ID$/) {
+ if ($error eq 'unknownID') {
+ $r->print("The encoded ID is not in the classlist\n");
+ } elsif ($error eq 'duplicateID') {
+ $r->print("The encoded ID has also been used by a previous paper $arg\n");
+ }
+ $r->print("
The ID on the form is ".
+ $$scan_record{'scantron.ID'}." \n");
+ $r->print("The name on the paper is ".
+ $$scan_record{'scantron.LastName'}.",".
+ $$scan_record{'scantron.FirstName'}."
");
+ $r->print("
How should I handle this? \n");
+ $r->print("\n
");
+ #FIXME it would be nice if this sent back the user ID and
+ #could do partial userID matches
+ $r->print(&Apache::loncommon::selectstudent_link('scantronupload',
+ 'scantron_username','scantron_domain'));
+ $r->print(": ");
+ $r->print("\n@".
+ &Apache::loncommon::select_dom_form(undef,'scantron_domain'));
+
+ $r->print('
');
+ } elsif ($error eq 'doublebubble') {
+#FIXME Need to print out who this is along with the paper info
+ $r->print("
There have been multiple bubbles scanned for a some question(s)
\n");
+ $r->print('');
+ $r->print("
Please indicate which bubble should be used for grading
result is'.$result);
+# &Apache::lonhomework::showhash(%score);
+ # if ($i eq 3) {last;}
+ }
+ $completedstudents{$uname}={'line'=>$line};
+ } continue {
+ &Apache::lonnet::delenv('form.counter');
+ &Apache::lonnet::delenv('scantron\.');
+ &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,$uname);
+ #last;
+ #FIXME
+ #get iterator for $sequence
+ #foreach question 'submit' the students answer to the server
+ # through grade target {
+ # generate data to pass back that includes grade recevied
+ #}
+ }
+ &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
+ my $lasttime = &Time::HiRes::time()-$start;
+ #$r->print("
took $lasttime
");
+
+ #$Apache::lonxml::debug=0;
+ foreach my $delay (@delayqueue) {
+ #FIXME
+ #print out each delayed student with interface to select how
+ # to repair student provided info
+ #Expected errors include
+ # 1 bad/no stuid/username
+ # 2 invalid bubblings
+
+ }
+ #FIXME
+ # if delay queue exists 2 submits one to process delayed students one
+ # to ignore delayed students, possibly saving the delay queue for later
+
+ $navmap->untieHashes();
+ $r->print("
Done
");
+ $r->print(&show_grading_menu_form($symb,$url));
+ return '';
+}
+
+sub scantron_upload_scantron_data {
+ my ($r)=@_;
+ $r->print(&Apache::loncommon::coursebrowser_javascript($ENV{'request.role.domain'}));
+ my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',
+ 'domainid');
+ my $domsel=&Apache::loncommon::select_dom_form($ENV{'request.role.domain'},
+ 'domainid');
+ $r->print(<
+ function checkUpload(formname) {
+ if (formname.upfile.value == "") {
+ alert("Please use the browse button to select a file from your local directory.");
+ return false;
+ }
+ formname.submit();
+ }
+
+
+
+UPLOAD
+ return '';
+}
+
+sub scantron_upload_scantron_data_save {
+ my($r)=@_;
+ $r->print("Doing upload to ".$ENV{'form.courseid'});
+ my $home=&Apache::lonnet::homeserver($ENV{'form.courseid'},
+ $ENV{'form.domainid'});
+ my $fname=$ENV{'form.upfile.filename'};
+ #FIXME
+ #copied from lonnet::userfileupload()
+ #make that function able to target a specified course
+ # Replace Windows backslashes by forward slashes
+ $fname=~s/\\/\//g;
+ # Get rid of everything but the actual filename
+ $fname=~s/^.*\/([^\/]+)$/$1/;
+ # Replace spaces by underscores
+ $fname=~s/\s+/\_/g;
+ # Replace all other weird characters by nothing
+ $fname=~s/[^\w\.\-]//g;
+ # See if there is anything left
+ unless ($fname) { return 'error: no uploaded file'; }
+ $fname='scantron_orig_'.$fname;
+ &Apache::lonnet::logthis("fname is $fname");
+ $r->print(&Apache::lonnet::finishuserfileupload($ENV{'form.courseid'},
+ $ENV{'form.domainid'},
+ $home,'upfile',$fname));
+ return '';
+}
+
+
+#-------- end of section for handling grading scantron forms -------
+#
+#-------------------------------------------------------------------
+
+
+#-------------------------- Menu interface -------------------------
+#
+#--- Show a Grading Menu button - Calls the next routine ---
+sub show_grading_menu_form {
+ my ($symb,$url)=@_;
+ my $result.='