--- loncom/homework/grades.pm 2003/09/29 21:31:30 1.130.2.1.2.6
+++ loncom/homework/grades.pm 2007/11/08 01:48:18 1.485
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.130.2.1.2.6 2003/09/29 21:31:30 albertel Exp $
+# $Id: grades.pm,v 1.485 2007/11/08 01:48:18 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -25,16 +25,6 @@
#
# http://www.lon-capa.org/
#
-# 2/9,2/13 Guy Albertelli
-# 6/8 Gerd Kortemeyer
-# 7/26 H.K. Ng
-# 8/20 Gerd Kortemeyer
-# Year 2002
-# June-August H.K. Ng
-# Year 2003
-# February, March H.K. Ng
-# July, H. K. Ng
-#
package Apache::grades;
use strict;
@@ -45,55 +35,135 @@ use Apache::loncommon;
use Apache::lonhtmlcommon;
use Apache::lonnavmaps;
use Apache::lonhomework;
+use Apache::lonpickcode;
use Apache::loncoursedata;
-use Apache::lonmsg qw(:user_normal_msg);
+use Apache::lonmsg();
use Apache::Constants qw(:common);
+use Apache::lonlocal;
+use Apache::lonenc;
use String::Similarity;
+use LONCAPA;
+
+use POSIX qw(floor);
+
-my %oldessays=();
my %perm=();
+my %bubble_lines_per_response = (); # no. bubble lines for each response.
+ # index is "symb.part_id"
+
+my %first_bubble_line = (); # First bubble line no. for each bubble.
+
+# Save and restore the bubble lines array to the form env.
+
+
+sub save_bubble_lines {
+ foreach my $line (keys(%bubble_lines_per_response)) {
+ $env{"form.scantron.bubblelines.$line"} = $bubble_lines_per_response{$line};
+ $env{"form.scantron.first_bubble_line.$line"} =
+ $first_bubble_line{$line};
+ }
+}
+
+
+sub restore_bubble_lines {
+ my $line = 0;
+ %bubble_lines_per_response = ();
+ while ($env{"form.scantron.bubblelines.$line"}) {
+ my $value = $env{"form.scantron.bubblelines.$line"};
+ $bubble_lines_per_response{$line} = $value;
+ $first_bubble_line{$line} =
+ $env{"form.scantron.first_bubble_line.$line"};
+ $line++;
+ }
+
+}
+
+# Given the parsed scanline, get the response for
+# 'answer' number n:
+
+sub get_response_bubbles {
+ my ($parsed_line, $response) = @_;
+
+
+ my $bubble_line = $first_bubble_line{$response-1} +1;
+ my $bubble_lines= $bubble_lines_per_response{$response-1};
+
+ my $selected = "";
+
+ for (my $bline = 0; $bline < $bubble_lines; $bline++) {
+ $selected .= $$parsed_line{"scantron.$bubble_line.answer"}.":";
+ $bubble_line++;
+ }
+ return $selected;
+}
+
# ----- These first few routines are general use routines.----
+
+# Return the number of occurences of a pattern in a string.
+
+sub occurence_count {
+ my ($string, $pattern) = @_;
+
+ my @matches = ($string =~ /$pattern/g);
+
+ return scalar(@matches);
+}
+
+
+# Take a string known to have digits and convert all the
+# digits into letters in the range J,A..I.
+
+sub digits_to_letters {
+ my ($input) = @_;
+
+ my @alphabet = ('J', 'A'..'I');
+
+ my @input = split(//, $input);
+ my $output ='';
+ for (my $i = 0; $i < scalar(@input); $i++) {
+ if ($input[$i] =~ /\d/) {
+ $output .= $alphabet[$input[$i]];
+ } else {
+ $output .= $input[$i];
+ }
+ }
+ return $output;
+}
+
#
-# --- Retrieve the parts that matches stores_\d+ from the metadata file.---
+# --- Retrieve the parts from the metadata file.---
sub getpartlist {
- my ($url) = @_;
- my @parts =();
- my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
- foreach my $key (@metakeys) {
- if ( $key =~ m/stores_(\w+)_.*/) {
- push(@parts,$key);
+ my ($symb) = @_;
+
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ my $res = $navmap->getBySymb($symb);
+ my $partlist = $res->parts();
+ my $url = $res->src();
+ my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys'));
+
+ my @stores;
+ foreach my $part (@{ $partlist }) {
+ foreach my $key (@metakeys) {
+ if ($key =~ m/^stores_\Q$part\E_/) { push(@stores,$key); }
}
}
- return @parts;
+ return @stores;
}
# --- Get the symbolic name of a problem and the url
-sub get_symb_and_url {
- my ($request) = @_;
- (my $url=$ENV{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
- 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 ''; }
- return ($symb,$url);
-}
-
-# --- Retrieve the fullname for a user. Return lastname, first middle ---
-# --- Generation is attached next to the lastname if it exists. ---
-sub get_fullname {
- my ($uname,$udom) = @_;
- my %name=&Apache::lonnet::get('environment', ['lastname','generation',
- 'firstname','middlename'],
- $udom,$uname);
- my $fullname;
- my ($tmp) = keys(%name);
- if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
- $fullname = &Apache::loncoursedata::ProcessFullName
- (@name{qw/lastname generation firstname middlename/});
- } else {
- &Apache::lonnet::logthis('grades.pm: no name data for '.$uname.
- '@'.$udom.':'.$tmp);
+sub get_symb {
+ my ($request,$silent) = @_;
+ (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
+ my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
+ if ($symb eq '') {
+ if (!$silent) {
+ $request->print("Unable to handle ambiguous references:$url:.");
+ return ();
+ }
}
- return $fullname;
+ &Apache::lonenc::check_decrypt(\$symb);
+ return ($symb);
}
#--- Format fullname, username:domain if different for display
@@ -101,91 +171,267 @@ sub get_fullname {
sub nameUserString {
my ($type,$fullname,$uname,$udom) = @_;
if ($type eq 'header') {
- return ' Fullname (Username) ';
+ return ' '.&mt('Fullname').' ('.&mt('Username').')';
} else {
- return ' '.$fullname.' ('.$uname.
- ($ENV{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')';
+ 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,$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);
- foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) {
- if (/^\w+response_\w+.*/) {
- my ($responsetype,$part) = split(/_/,$_,2);
- my ($partid,$respid) = split(/_/,$part);
- $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;
- }
+ my ($symb) = shift;
+
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ my $res = $navmap->getBySymb($symb);
+ my $partlist = $res->parts();
+ my %vPart =
+ map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart'));
+ my (%response_types,%handgrade);
+ foreach my $part (@{ $partlist }) {
+ next if (%vPart && !exists($vPart{$part}));
+
+ my @types = $res->responseType($part);
+ my @ids = $res->responseIds($part);
+ for (my $i=0; $i < scalar(@ids); $i++) {
+ $response_types{$part}{$ids[$i]} = $types[$i];
+ $handgrade{$part.'_'.$ids[$i]} =
+ &Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i].
+ '.handgrade',$symb);
+ }
+ }
+ return ($partlist,\%handgrade,\%response_types);
+}
+
+sub flatten_responseType {
+ my ($responseType) = @_;
+ my @part_response_id =
+ map {
+ my $part = $_;
+ map {
+ [$part,$_]
+ } sort(keys(%{ $responseType->{$part} }));
+ } sort(keys(%$responseType));
+ return @part_response_id;
+}
+
+sub get_display_part {
+ my ($partID,$symb)=@_;
+ my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb);
+ if (defined($display) and $display ne '') {
+ $display.= " (id $partID)";
+ } else {
+ $display=$partID;
}
- return \@partlist,\%handgrade;
+ return $display;
}
#--- 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 ($symb,$probTitle,$checkboxes) = @_;
+ my $col=3;
+ if ($checkboxes) { $col=4; }
+ my $result = '
'.&mt('Current Resource').': '.$probTitle.'
'."\n";
+ $result .='
';
+ my ($partlist,$handgrade,$responseType) = &response_type($symb);
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.'
';
+ my %partsseen;
+ foreach my $partID (sort keys(%$responseType)) {
+ foreach my $resID (sort keys(%{ $responseType->{$partID} })) {
+ my $handgrade=$$handgrade{$partID.'_'.$resID};
+ my $responsetype = $responseType->{$partID}->{$resID};
+ $hdgrade = $handgrade if ($handgrade eq 'yes');
+ $result.='
';
+ if ($checkboxes) {
+ if (exists($partsseen{$partID})) {
+ $result.="
'."\n";
- return $result,\%resptype,$hdgrade,$partlist,$handgrade;
+ return $result,$responseType,$hdgrade,$partlist,$handgrade;
+}
+
+sub reset_caches {
+ &reset_analyze_cache();
+ &reset_perm();
+}
+
+{
+ my %analyze_cache;
+
+ sub reset_analyze_cache {
+ undef(%analyze_cache);
+ }
+
+ sub get_analyze {
+ my ($symb,$uname,$udom)=@_;
+ my $key = "$symb\0$uname\0$udom";
+ return $analyze_cache{$key} if (exists($analyze_cache{$key}));
+
+ 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_cache{$key} = \%analyze;
+ }
+
+ sub get_order {
+ my ($partid,$respid,$symb,$uname,$udom)=@_;
+ my $analyze = &get_analyze($symb,$uname,$udom);
+ return $analyze->{"$partid.$respid.shown"};
+ }
+
+ sub get_radiobutton_correct_foil {
+ my ($partid,$respid,$symb,$uname,$udom)=@_;
+ my $analyze = &get_analyze($symb,$uname,$udom);
+ foreach my $foil (@{&get_order($partid,$respid,$symb,$uname,$udom)}) {
+ if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') {
+ return $foil;
+ }
+ }
+ }
}
#--- Clean response type for display
-#--- Currently filters option response type only.
+#--- Currently filters option/rank/radiobutton/match/essay/Task
+# 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.'';
- push @ans,$ans;
+ my ($answer,$response,$symb,$partid,$respid,$record,$order,$version,
+ $uname,$udom) = @_;
+ my $grayFont = '';
+ 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.='
'.$answer{$foil}.'
';
+ } else {
+ $toprow.='
'.$answer{$foil}.'
';
+ }
+ $bottomrow.='
'.$grayFont.$foil.'
';
}
- my $grayFont = '';
return '
'.
- '
Answer
'.
- (join '
',@ans).'
'.
- '
'.$grayFont.'Option ID
'.$grayFont.
- (join '
'.$grayFont,@IDs).'
'.
- '
';
- }
- if ($response eq 'essay') {
- if (! exists ($ENV{'form.'.$symb})) {
+ '
'.&mt('Answer').'
'.$toprow.'
'.
+ '
'.$grayFont.&mt('Option ID').'
'.
+ $grayFont.$bottomrow.'
'.'
';
+ } 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.='
'.$item.'
';
+ $middlerow.='
'.$grayFont.$answer{$foil}.'
';
+ } else {
+ $toprow.='
'.$item.'
';
+ $middlerow.='
'.$grayFont.$answer{$foil}.'
';
+ }
+ $bottomrow.='
'.$grayFont.$foil.'
';
+ }
+ return '
'.
+ '
'.&mt('Answer').'
'.$toprow.'
'.
+ '
'.$grayFont.&mt('Item ID').'
'.
+ $middlerow.'
'.
+ '
'.$grayFont.&mt('Option ID').'
'.
+ $bottomrow.'
'.'
';
+ } elsif ($response eq 'radiobutton') {
+ my %answer=&Apache::lonnet::str2hash($answer);
+ my ($toprow,$bottomrow);
+ my $correct =
+ &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom);
+ foreach my $foil (@$order) {
+ if (exists($answer{$foil})) {
+ if ($foil eq $correct) {
+ $toprow.='
'.&mt('true').'
';
+ } else {
+ $toprow.='
'.&mt('true').'
';
+ }
+ } else {
+ $toprow.='
'.&mt('false').'
';
+ }
+ $bottomrow.='
'.$grayFont.$foil.'
';
+ }
+ return '
'.
+ '
'.&mt('Answer').'
'.$toprow.'
'.
+ '
'.$grayFont.&mt('Option ID').'
'.
+ $grayFont.$bottomrow.'
'.'
';
+ } elsif ($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'});
+ $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).'
';
+ 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.
+ }
+ $answer =~ s-\n- -g;
+ return '
';
+ my @grade = grep(/^\Q${version}resource.$respid.$partid.\E[^.]*[.]status$/,
+ keys(%{$record}));
+ foreach my $grade (sort(@grade)) {
+ my ($dim) = ($grade =~/[.]([^.]+)[.]status$/);
+ $result.= '
'.&mt("Dimension: [_1], status [_2] ",
+ $dim, $record->{$grade}).
+ '
';
+ }
+ $result.='
';
+ return $result;
+ }
+ } elsif ( $response =~ m/(?:numerical|formula)/) {
+ $answer =
+ &Apache::loncommon::format_previous_attempt_value('submission',
+ $answer);
}
return $answer;
}
@@ -218,7 +464,8 @@ sub commonJSfunctions {
}
}
} else {
- if (selectOne.selected) return selectOne.value;
+ // only one value it must be the selected one
+ return selectOne.value;
}
}
@@ -228,35 +475,86 @@ COMMONJSFUNCTIONS
#--- Dumps the class list with usernames,list of sections,
#--- section, ids and fullnames for each user.
sub getclasslist {
- my ($getsec,$filterlist) = @_;
- $getsec = $getsec eq '' ? 'all' : $getsec;
- my $classlist=&Apache::loncoursedata::get_classlist();
+ my ($getsec,$filterlist,$getgroup) = @_;
+ my @getsec;
+ my @getgroup;
+ my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
+ if (!ref($getsec)) {
+ if ($getsec ne '' && $getsec ne 'all') {
+ @getsec=($getsec);
+ }
+ } else {
+ @getsec=@{$getsec};
+ }
+ if (grep(/^all$/,@getsec)) { undef(@getsec); }
+ if (!ref($getgroup)) {
+ if ($getgroup ne '' && $getgroup ne 'all') {
+ @getgroup=($getgroup);
+ }
+ } else {
+ @getgroup=@{$getgroup};
+ }
+ if (grep(/^all$/,@getgroup)) { undef(@getgroup); }
+
+ my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist();
# Bail out if we were unable to get the classlist
return if (! defined($classlist));
+ &Apache::loncoursedata::get_group_memberships($classlist,$keylist);
#
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->{$_}};
+ foreach my $student (keys(%$classlist)) {
+ my $end =
+ $classlist->{$student}->[&Apache::loncoursedata::CL_END()];
+ my $start =
+ $classlist->{$student}->[&Apache::loncoursedata::CL_START()];
+ my $id =
+ $classlist->{$student}->[&Apache::loncoursedata::CL_ID()];
+ my $section =
+ $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
+ my $fullname =
+ $classlist->{$student}->[&Apache::loncoursedata::CL_FULLNAME()];
+ my $status =
+ $classlist->{$student}->[&Apache::loncoursedata::CL_STATUS()];
+ my $group =
+ $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
# filter students according to status selected
- if ($filterlist && $ENV{'form.Status'} ne 'Any') {
- if ($ENV{'form.Status'} ne $status) {
- delete ($classlist->{$_});
+ if ($filterlist && (!($stu_status =~ /Any/))) {
+ if (!($stu_status =~ $status)) {
+ delete($classlist->{$student});
next;
}
}
- $section = ($section ne '' ? $section : 'no');
+ # filter students according to groups selected
+ my @stu_groups = split(/,/,$group);
+ if (@getgroup) {
+ my $exclude = 1;
+ foreach my $grp (@getgroup) {
+ foreach my $stu_group (@stu_groups) {
+ if ($stu_group eq $grp) {
+ $exclude = 0;
+ }
+ }
+ if (($grp eq 'none') && !$group) {
+ $exclude = 0;
+ }
+ }
+ if ($exclude) {
+ delete($classlist->{$student});
+ }
+ }
+ $section = ($section ne '' ? $section : 'none');
if (&canview($section)) {
- if ($getsec eq 'all' || $getsec eq $section) {
+ if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {
$sections{$section}++;
- $fullnames{$_}=$fullname;
+ if ($classlist->{$student}) {
+ $fullnames{$student}=$fullname;
+ }
} else {
- delete($classlist->{$_});
+ delete($classlist->{$student});
}
} else {
- delete($classlist->{$_});
+ delete($classlist->{$student});
}
}
my %seen = ();
@@ -306,8 +604,8 @@ sub canview {
#--- Retrieve the grade status of a student for all the parts
sub student_gradeStatus {
- my ($url,$symb,$udom,$uname,$partlist) = @_;
- my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
+ my ($symb,$udom,$uname,$partlist) = @_;
+ my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
my %partstatus = ();
foreach (@$partlist) {
my ($status,undef) = split(/_/,$record{"resource.$_.solved"},2);
@@ -323,7 +621,8 @@ sub student_gradeStatus {
# Use by verifyscript and viewgrades
# Shows a student's view of problem and submission
sub jscriptNform {
- my ($url,$symb) = @_;
+ my ($symb) = @_;
+ my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
my $jscript=''."\n";
$jscript.= ''."\n";
if ($ctr == 0) {
my $num_students=(scalar(keys(%$fullname)));
if ($num_students eq 0) {
- $gradeTable=' There are no students currently enrolled.';
+ $gradeTable=' '.&mt('There are no students currently enrolled.').'';
} else {
- $gradeTable=' '.
- 'No submissions found for this resource for any students. ('.$num_students.
- ' checked for submissions ';
+ my $submissions='submissions';
+ if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; }
+ if ($submitonly eq 'graded' ) { $submissions = 'ungraded submissions'; }
+ if ($submitonly eq 'queued' ) { $submissions = 'queued submissions'; }
+ $gradeTable=' '.
+ &mt('No '.$submissions.' found for this resource for any students. ([_1] students checked for '.$submissions.')',
+ $num_students).
+ ' ';
}
} elsif ($ctr == 1) {
- $gradeTable =~ s/type=checkbox/type=checkbox checked/;
+ $gradeTable =~ s/type="checkbox"/type="checkbox" checked="checked"/;
}
- $gradeTable.=&show_grading_menu_form($symb,$url);
+ $gradeTable.=&show_grading_menu_form($symb);
$request->print($gradeTable);
return '';
}
#---- Called from the listStudents routine
+
+sub check_script {
+ my ($form, $type)=@_;
+ my $chkallscript=''."\n";
+ return $chkallscript;
+}
+
+sub check_buttons {
+ my $buttons.='';
+ $buttons.=' ';
+ $buttons.='';
+ $buttons.=' ';
+ return $buttons;
+}
+
# Displays the submissions for one student or a group of students
sub processGroup {
my ($request) = shift;
my $ctr = 0;
- my @stuchecked = (ref($ENV{'form.stuinfo'}) ? @{$ENV{'form.stuinfo'}}
- : ($ENV{'form.stuinfo'}));
+ my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');
my $total = scalar(@stuchecked)-1;
- foreach (@stuchecked) {
- my ($uname,$udom,$fullname) = split(/:/);
- $ENV{'form.student'} = $uname;
- $ENV{'form.userdom'} = $udom;
- $ENV{'form.fullname'} = $fullname;
+ foreach my $student (@stuchecked) {
+ my ($uname,$udom,$fullname) = split(/:/,$student);
+ $env{'form.student'} = $uname;
+ $env{'form.userdom'} = $udom;
+ $env{'form.fullname'} = $fullname;
&submission($request,$ctr,$total);
$ctr++;
}
@@ -837,6 +1318,83 @@ sub sub_page_kw_js {
my $request = shift;
my $iconpath = $request->dir_config('lonIconsURL');
&commonJSfunctions($request);
+
+ my $inner_js_msg_central=<
+ function checkInput() {
+ opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value);
+ var nmsg = opener.document.SCORE.savemsgN.value;
+ var usrctr = document.msgcenter.usrctr.value;
+ var newval = opener.document.SCORE["newmsg"+usrctr];
+ newval.value = opener.checkEntities(document.msgcenter.newmsg.value);
+
+ var msgchk = "";
+ if (document.msgcenter.subchk.checked) {
+ msgchk = "msgsub,";
+ }
+ var includemsg = 0;
+ for (var i=1; i<=nmsg; i++) {
+ var opnmsg = opener.document.SCORE["savemsg"+i];
+ var frmmsg = document.msgcenter["msg"+i];
+ opnmsg.value = opener.checkEntities(frmmsg.value);
+ var showflg = opener.document.SCORE["shownOnce"+i];
+ showflg.value = "1";
+ var chkbox = document.msgcenter["msgn"+i];
+ if (chkbox.checked) {
+ msgchk += "savemsg"+i+",";
+ includemsg = 1;
+ }
+ }
+ if (document.msgcenter.newmsgchk.checked) {
+ msgchk += "newmsg"+usrctr;
+ includemsg = 1;
+ }
+ imgformname = opener.document.SCORE["mailicon"+usrctr];
+ imgformname.src = "$iconpath/"+((includemsg) ? "mailto.gif" : "mailbkgrd.gif");
+ var includemsg = opener.document.SCORE["includemsg"+usrctr];
+ includemsg.value = msgchk;
+
+ self.close()
+
+ }
+
+INNERJS
+
+ my $inner_js_highlight_central=<
+ function updateChoice(flag) {
+ opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);
+ opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);
+ opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);
+ opener.document.SCORE.refresh.value = "on";
+ if (opener.document.SCORE.keywords.value!=""){
+ opener.document.SCORE.submit();
+ }
+ self.close()
+ }
+
+INNERJS
+
+ my $start_page_msg_central =
+ &Apache::loncommon::start_page('Message Central',$inner_js_msg_central,
+ {'js_ready' => 1,
+ 'only_body' => 1,
+ 'bgcolor' =>'#FFFFFF',});
+ my $end_page_msg_central =
+ &Apache::loncommon::end_page({'js_ready' => 1});
+
+
+ my $start_page_highlight_central =
+ &Apache::loncommon::start_page('Highlight Central',
+ $inner_js_highlight_central,
+ {'js_ready' => 1,
+ 'only_body' => 1,
+ 'bgcolor' =>'#FFFFFF',});
+ my $end_page_highlight_central =
+ &Apache::loncommon::end_page({'js_ready' => 1});
+
+ my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
+ $docopen=~s/^document\.//;
$request->print(<
@@ -946,96 +1504,52 @@ sub sub_page_kw_js {
var ypos = (screen.height-height)/2-30;
ypos = (ypos < 0) ? '0' : ypos;
- pWin = window.open('', 'MessageCenter', 'toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height);
+ pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height);
pWin.focus();
pDoc = pWin.document;
- pDoc.open('text/html','replace');
- pDoc.write("");
- pDoc.write("Message Central");
-
- pDoc.write("
CSVFORMJS
- $ENV{'form.probTitle'} = &Apache::lonnet::gettitle($symb);
- my ($table) = &showResourceInfo($url,$ENV{'form.probTitle'});
+ return $result;
+}
+
+sub upcsvScores_form {
+ my ($request) = shift;
+ my ($symb)=&get_symb($request);
+ if (!$symb) {return '';}
+ my $result=&checkforfile_js();
+ $env{'form.probTitle'} = &Apache::lonnet::gettitle($symb);
+ my ($table) = &showResourceInfo($symb,$env{'form.probTitle'});
$result.=$table;
- $result.='
'."\n";
- $result.='
'."\n";
- $result.=' Specify a file containing the class scores for current resource'.
+ $result.='
'."\n";
+ $result.='
'."\n";
+ $result.=' '.&mt('Specify a file containing the class scores for current resource').
'.
'."\n";
$result.='
'."\n";
+ my $upload=&mt("Upload Scores");
my $upfile_select=&Apache::loncommon::upfile_select_html();
+ my $ignore=&mt('Ignore First Line');
+ $symb = &Apache::lonenc::check_encrypt($symb);
$result.=<
-
-
-
+
+
$upfile_select
-
-
+
+
ENDUPFORM
- $result.='
'."\n";
+ $result.=&Apache::loncommon::help_open_topic("Course_Convert_To_CSV",
+ &mt("How do I create a CSV file from a spreadsheet"))
+ .'
'."\n";
$result.='
'."\n";
- $result.=&show_grading_menu_form($symb,$url);
+ $result.=&show_grading_menu_form($symb);
return $result;
}
sub csvuploadmap {
my ($request)= @_;
- my ($symb,$url)=&get_symb_and_url($request);
+ my ($symb)=&get_symb($request);
if (!$symb) {return '';}
my $datatoken;
- if (!$ENV{'form.datatoken'}) {
+ if (!$env{'form.datatoken'}) {
$datatoken=&Apache::loncommon::upfile_store($request);
} else {
- $datatoken=$ENV{'form.datatoken'};
+ $datatoken=$env{'form.datatoken'};
&Apache::loncommon::load_tmp_file($request);
}
my @records=&Apache::loncommon::upfile_record_sep();
- &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);
+ if ($env{'form.noFirstLine'}) { shift(@records); }
+ &csvuploadmap_header($request,$symb,$datatoken,$#records+1);
my ($i,$keyfields);
if (@records) {
- my @fields=&csvupload_fields($url);
+ my @fields=&csvupload_fields($symb);
- if ($ENV{'form.upfile_associate'} eq 'reverse') {
+ if ($env{'form.upfile_associate'} eq 'reverse') {
&Apache::loncommon::csv_print_samples($request,\@records);
$i=&Apache::loncommon::csv_print_select_table($request,\@records,
\@fields);
@@ -2611,47 +3872,122 @@ sub csvuploadmap {
unshift(@fields,['none','']);
$i=&Apache::loncommon::csv_samples_select_table($request,\@records,
\@fields);
- my %sone=&Apache::loncommon::record_sep($records[0]);
- $keyfields=join(',',sort(keys(%sone)));
+ foreach my $rec (@records) {
+ my %temp = &Apache::loncommon::record_sep($rec);
+ if (%temp) {
+ $keyfields=join(',',sort(keys(%temp)));
+ last;
+ }
+ }
}
}
&csvuploadmap_footer($request,$i,$keyfields);
- $request->print(&show_grading_menu_form($symb,$url));
+ $request->print(&show_grading_menu_form($symb));
return '';
}
-sub csvuploadassign {
+sub csvuploadoptions {
my ($request)= @_;
- my ($symb,$url)=&get_symb_and_url($request);
- if (!$symb) {return '';}
- &Apache::loncommon::load_tmp_file($request);
- my @gradedata = &Apache::loncommon::upfile_record_sep();
- my @keyfields = split(/\,/,$ENV{'form.keyfields'});
- my %fields=();
- for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) {
- if ($ENV{'form.upfile_associate'} eq 'reverse') {
- if ($ENV{'form.f'.$i} ne 'none') {
- $fields{$keyfields[$i]}=$ENV{'form.f'.$i};
+ my ($symb)=&get_symb($request);
+ my $checked=(($env{'form.noFirstLine'})?'1':'0');
+ my $ignore=&mt('Ignore First Line');
+ $request->print(<
+
Uploading Class Grade Options
+
+
+
+
+
+ENDPICK
+ my %fields=&get_fields();
+ if (!defined($fields{'domain'})) {
+ my $domform = &Apache::loncommon::select_dom_form($env{'request.role.domain'},'default_domain');
+ $request->print("\n
Users are in domain: ".$domform."
\n");
+ }
+ foreach my $key (sort(keys(%env))) {
+ if ($key !~ /^form\.(.*)$/) { next; }
+ my $cleankey=$1;
+ if ($cleankey eq 'command') { next; }
+ $request->print(''."\n");
+ }
+ # FIXME do a check for any duplicated user ids...
+ # FIXME do a check for any invalid user ids?...
+ $request->print('
+'."\n");
+ $request->print(&show_grading_menu_form($symb));
+ return '';
+}
+
+sub get_fields {
+ my %fields;
+ my @keyfields = split(/\,/,$env{'form.keyfields'});
+ for (my $i=0; $i<=$env{'form.nfields'}; $i++) {
+ if ($env{'form.upfile_associate'} eq 'reverse') {
+ if ($env{'form.f'.$i} ne 'none') {
+ $fields{$keyfields[$i]}=$env{'form.f'.$i};
}
} else {
- if ($ENV{'form.f'.$i} ne 'none') {
- $fields{$ENV{'form.f'.$i}}=$keyfields[$i];
+ if ($env{'form.f'.$i} ne 'none') {
+ $fields{$env{'form.f'.$i}}=$keyfields[$i];
}
}
}
+ return %fields;
+}
+
+sub csvuploadassign {
+ my ($request)= @_;
+ my ($symb)=&get_symb($request);
+ if (!$symb) {return '';}
+ my $error_msg = '';
+ &Apache::loncommon::load_tmp_file($request);
+ my @gradedata = &Apache::loncommon::upfile_record_sep();
+ if ($env{'form.noFirstLine'}) { shift(@gradedata); }
+ my %fields=&get_fields();
$request->print('
Assigning Grades
');
- my $courseid=$ENV{'request.course.id'};
+ my $courseid=$env{'request.course.id'};
my ($classlist) = &getclasslist('all',0);
my @notallowed;
my @skipped;
my $countdone=0;
foreach my $grade (@gradedata) {
my %entries=&Apache::loncommon::record_sep($grade);
+ my $domain;
+ if ($entries{$fields{'domain'}}) {
+ $domain=$entries{$fields{'domain'}};
+ } else {
+ $domain=$env{'form.default_domain'};
+ }
+ $domain=~s/\s//g;
my $username=$entries{$fields{'username'}};
- my $domain=$entries{$fields{'domain'}};
+ $username=~s/\s//g;
+ if (!$username) {
+ my $id=$entries{$fields{'ID'}};
+ $id=~s/\s//g;
+ my %ids=&Apache::lonnet::idget($domain,$id);
+ $username=$ids{$id};
+ }
if (!exists($$classlist{"$username:$domain"})) {
- push(@skipped,"$username:$domain");
+ my $id=$entries{$fields{'ID'}};
+ $id=~s/\s//g;
+ if ($id) {
+ push(@skipped,"$id:$domain");
+ } else {
+ push(@skipped,"$username:$domain");
+ }
next;
}
my $usec=$classlist->{"$username:$domain"}[5];
@@ -2659,34 +3995,70 @@ 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}};
- }
- $grades{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}";
- &Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'},
- $domain,$username);
- $request->print('.');
+ 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);
+ if ($wgt) {
+ $entries{$fields{$dest}}=~s/\s//g;
+ my $pcr=$entries{$fields{$dest}} / $wgt;
+ my $award=($pcr == 0) ? 'incorrect_by_override'
+ : 'correct_by_override';
+ $grades{"resource.$part.awarded"}=$pcr;
+ $grades{"resource.$part.solved"}=$award;
+ $points{$part}=1;
+ } else {
+ $error_msg = " " .
+ &mt("Some point values were assigned"
+ ." for problems with a weight "
+ ."of zero. These values were "
+ ."ignored.");
+ }
+ } 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 save"); }
+ $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
+ my $result=&Apache::lonnet::cstore(\%grades,$symb,
+ $env{'request.course.id'},
+ $domain,$username);
+ if ($result eq 'ok') {
+ $request->print('.');
+ } else {
+ $request->print("
+
+ Failed to save student $username:$domain.
+ Message when trying to save was ($result)
+
+
';
+ my $studentTable.=' '.&mt('Select a student you wish to grade and then click on the Next button.').' '.
+ &Apache::loncommon::start_data_table().
+ &Apache::loncommon::start_data_table_header_row().
+ '
'.&mt('No.').'
'.
+ '
'.&nameUserString('header').'
'.
+ '
'.&mt('No.').'
'.
+ '
'.&nameUserString('header').'
'.
+ &Apache::loncommon::end_data_table_header_row();
my (undef,undef,$fullname) = &getclasslist($getsec,'1');
my $ptr = 1;
- foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
+ foreach my $student (sort
+ {
+ if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
+ return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
+ }
+ return $a cmp $b;
+ } (keys(%$fullname))) {
my ($uname,$udom) = split(/:/,$student);
- $studentTable.=($ptr%2 == 1 ? '
'.
+ &Apache::loncommon::end_data_table_header_row();
- my ($depth,$question) = (1,1);
+ &Apache::lonxml::clear_problem_counter();
+ my ($depth,$question,$prob) = (1,1,1);
$iterator->next(); # skip the first BEGIN_MAP
my $curRes = $iterator->next(); # for "current resource"
while ($depth > 0) {
@@ -2885,39 +4298,54 @@ sub displayPage {
my $parts = $curRes->parts();
my $title = $curRes->compTitle();
my $symbx = $curRes->symb();
- $studentTable.='
';
- $studentTable.=&show_grading_menu_form($ENV{'form.symb'},$ENV{'form.url'});
+ $studentTable.=&Apache::loncommon::end_data_table();
+ $studentTable.=&show_grading_menu_form($env{'form.symb'});
my $grademsg=($changeflag == 0 ? 'No score was changed or updated.' :
'The scores were changed for '.
$changeflag.' problem'.($changeflag == 1 ? '.' : 's.'));
@@ -3119,25 +4649,94 @@ sub updateGradeByPage {
#
#------ start of section for handling grading by page/sequence ---------
+=pod
+
+=head1 Bubble sheet grading routines
+
+ For this documentation:
+
+ 'scanline' refers to the full line of characters
+ from the file that we are parsing that represents one entire sheet
+
+ 'bubble line' refers to the data
+ representing the line of bubbles that are on the physical bubble sheet
+
+
+The overall process is that a scanned in bubble sheet data is uploaded
+into a course. When a user wants to grade, they select a
+sequence/folder of resources, a file of bubble sheet info, and pick
+one of the predefined configurations for what each scanline looks
+like.
+
+Next each scanline is checked for any errors of either 'missing
+bubbles' (it's an error because it may have been mis-scanned
+because too light bubbling), 'double bubble' (each bubble line should
+have no more that one letter picked), invalid or duplicated CODE,
+invalid student ID
+
+If the CODE option is used that determines the randomization of the
+homework problems, either way the student ID is looked up into a
+username:domain.
+
+During the validation phase the instructor can choose to skip scanlines.
+
+After the validation phase, there are now 3 bubble sheet files
+
+ scantron_original_filename (unmodified original file)
+ scantron_corrected_filename (file where the corrected information has replaced the original information)
+ scantron_skipped_filename (contains the exact text of scanlines that where skipped)
+
+Also there is a separate hash nohist_scantrondata that contains extra
+correction information that isn't representable in the bubble sheet
+file (see &scantron_getfile() for more information)
+
+After all scanlines are either valid, marked as valid or skipped, then
+foreach line foreach problem in the picked sequence, an ssi request is
+made that simulates a user submitting their selected letter(s) against
+the homework problem.
+
+=over 4
+
+
+
+=item defaultFormData
+
+ Returns html hidden inputs used to hold context/default values.
+
+ Arguments:
+ $symb - $symb of the current resource
+
+=cut
+
sub defaultFormData {
- my ($symb,$url)=@_;
- return '
- '."\n".
- ''."\n".
- ''."\n".
- ''."\n";
+ my ($symb)=@_;
+ return ''."\n".
+ ''."\n".
+ ''."\n";
}
+
+=pod
+
+=item getSequenceDropDown
+
+ Return html dropdown of possible sequences to grade
+
+ Arguments:
+ $symb - $symb of the current resource
+
+=cut
+
sub getSequenceDropDown {
- my ($request,$symb)=@_;
+ my ($symb)=@_;
my $result=''."\n";
- my ($titles,$symbx) = &getSymbMap($request);
- my ($curpage,$type,$mapId) = ($symb =~ /(.*?\.(page|sequence))___(\d+)___/);
+ my ($titles,$symbx) = &getSymbMap();
+ my ($curpage)=&Apache::lonnet::decode_symb($symb);
my $ctr=0;
foreach (@$titles) {
my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
$result.=''."\n";
$ctr++;
}
@@ -3145,25 +4744,65 @@ sub getSequenceDropDown {
return $result;
}
+
+=pod
+
+=item scantron_filenames
+
+ Returns a list of the scantron files in the current course
+
+=cut
+
+sub scantron_filenames {
+ my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
+ my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname,
+ &propath($cdom,$cname));
+ my @possiblenames;
+ foreach my $filename (sort(@files)) {
+ ($filename)=split(/&/,$filename);
+ if ($filename!~/^scantron_orig_/) { next ; }
+ $filename=~s/^scantron_orig_//;
+ push(@possiblenames,$filename);
+ }
+ return @possiblenames;
+}
+
+=pod
+
+=item scantron_uploads
+
+ Returns html drop-down list of scantron files in current course.
+
+ Arguments:
+ $file2grade - filename to set as selected in the dropdown
+
+=cut
+
sub scantron_uploads {
- #FIXME need to support scantron files put in another location,
- # maybe the course directory? a scantron dir in the course directory?
- if (!-e $Apache::lonnet::perlvar{'lonScansDir'}) { return ''};
+ my ($file2grade) = @_;
my $result= '';
- opendir(DIR,$Apache::lonnet::perlvar{'lonScansDir'});
- my @files=sort(readdir(DIR));
- foreach my $filename (@files) {
- if ($filename eq '.' or $filename eq '..') { next; }
- $result.="\n";
+ $result.="";
+ foreach my $filename (sort(&scantron_filenames())) {
+ $result.="\n";
}
- closedir(DIR);
$result.="";
return $result;
}
+=pod
+
+=item scantron_scantab
+
+ Returns html drop down of the scantron formats in the scantronformat.tab
+ file.
+
+=cut
+
sub scantron_scantab {
my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
my $result=''."\n";
+ $result.=''."\n";
foreach my $line (<$fh>) {
my ($name,$descrip)=split(/:/,$line);
if ($name =~ /^\#/) { next; }
@@ -3174,64 +4813,273 @@ sub scantron_scantab {
return $result;
}
+=pod
+
+=item scantron_CODElist
+
+ Returns html drop down of the saved CODE lists from current course,
+ generated from earlier printings.
+
+=cut
+
+sub scantron_CODElist {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my @names=&Apache::lonnet::getkeys('CODEs',$cdom,$cnum);
+ my $namechoice='';
+ foreach my $name (sort {uc($a) cmp uc($b)} @names) {
+ if ($name =~ /^error: 2 /) { next; }
+ if ($name =~ /^type\0/) { next; }
+ $namechoice.='';
+ }
+ $namechoice=''.$namechoice.'';
+ return $namechoice;
+}
+
+=pod
+
+=item scantron_CODEunique
+
+ Returns the html for "Each CODE to be used once" radio.
+
+=cut
+
+sub scantron_CODEunique {
+ my $result='
+
+
+
+
+ ';
+ return $result;
+}
+
+=pod
+
+=item scantron_selectphase
+
+ Generates the initial screen to start the bubble sheet process.
+ Allows for - starting a grading run.
+ - downloading existing scan data (original, corrected
+ or skipped info)
+
+ - uploading new scan data
+
+ Arguments:
+ $r - The Apache request object
+ $file2grade - name of the file that contain the scanned data to score
+
+=cut
+
sub scantron_selectphase {
- my ($r) = @_;
- my ($symb,$url)=&get_symb_and_url($r);
+ my ($r,$file2grade) = @_;
+ my ($symb)=&get_symb($r);
if (!$symb) {return '';}
- my $sequence_selector=&getSequenceDropDown($r,$symb);
- my $default_form_data=&defaultFormData($symb,$url);
- my $grading_menu_button=&show_grading_menu_form($symb,$url);
- my $file_selector=&scantron_uploads();
+ my $sequence_selector=&getSequenceDropDown($symb);
+ my $default_form_data=&defaultFormData($symb);
+ my $grading_menu_button=&show_grading_menu_form($symb);
+ my $file_selector=&scantron_uploads($file2grade);
my $format_selector=&scantron_scantab();
+ my $CODE_selector=&scantron_CODElist();
+ my $CODE_unique=&scantron_CODEunique();
my $result;
- #FIXME allow instructor to be able to download the scantron file
- # and to upload it,
+
+ # Chunk of form to prompt for a file to grade and how:
+
$result.= <
-
- $default_form_data
-