'
.&mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)
."
\n";
}
- # If any part of the problem is an essay-response (handgraded), then check for collaborators
+ # If any part of the problem is an essayresponse, then check for collaborators
my $fullname;
my $col_fullnames = [];
-# if ($env{'form.handgrade'} eq 'yes') {
- if (1) {
+ if ($numessay) {
(my $sub_result,$fullname,$col_fullnames)=
&check_collaborators($symb,$uname,$udom,\%record,$handgrade,
$counter);
$result.=$sub_result;
}
$request->print($result."\n");
-
- # print student answer/submission
- # Options are (1) Handgraded submission only
- # (2) Last submission, includes submission that is not handgraded
- # (for multi-response type part)
- # (3) Last submission plus the parts info
- # (4) The whole record for this student
-
- my ($string,$timestamp)= &get_last_submission(\%record);
-
- my $lastsubonly;
-
- if ($$timestamp eq '') {
- $lastsubonly.=''
.'
'.&mt('Assign Grades').'
'
@@ -2385,8 +2632,6 @@ sub submission {
my $part_resp = join('_',@{ $part_response_id });
next if ($seen{$partid} > 0);
$seen{$partid}++;
- next if ($$handgrade{$part_resp} ne 'yes'
- && $env{'form.lastSub'} eq 'hdgrade');
push(@partlist,$partid);
push(@gradePartRespid,$partid.'.'.$respid);
$request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
@@ -2442,6 +2687,186 @@ sub submission {
return '';
}
+sub show_last_submission {
+ my ($uname,$udom,$symb,$essayurl,$responseType,$viewtype,$is_tool,$fullname,
+ $record,$coursedesc_by_cid) = @_;
+ my ($string,$timestamp,$lastgradetime,$lastsubmittime) =
+ &get_last_submission($record,$is_tool);
+
+ my ($lastsubonly,$partinfo);
+ if ($timestamp eq '') {
+ $lastsubonly.='
'.$string->[0].'
';
+ } elsif ($is_tool) {
+ $lastsubonly =
+ '
'
+ .''.&mt('Date Grade Passed Back:').' '.$timestamp."
\n";
+ } else {
+ my ($shownsubmdate,$showngradedate);
+ if ($lastsubmittime && $lastgradetime) {
+ $shownsubmdate = &Apache::lonlocal::locallocaltime($lastsubmittime);
+ if ($lastgradetime > $lastsubmittime) {
+ $showngradedate = &Apache::lonlocal::locallocaltime($lastgradetime);
+ }
+ } else {
+ $shownsubmdate = $timestamp;
+ }
+ $lastsubonly =
+ '
'
+ .'
'.&mt('Date Submitted:').' '.$shownsubmdate."\n";
+ if ($showngradedate) {
+ $lastsubonly .= '
'.&mt('Date Graded:').' '.$showngradedate."\n";
+ }
+
+ my %seenparts;
+ my @part_response_id = &flatten_responseType($responseType);
+ foreach my $part (@part_response_id) {
+ my ($partid,$respid) = @{ $part };
+ my $display_part=&get_display_part($partid,$symb);
+ if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
+ if (exists($seenparts{$partid})) { next; }
+ $seenparts{$partid}=1;
+ $partinfo .=
+ '
'.&mt('Part: [_1]',$display_part).''.
+ '
'.&mt('Collaborative submission by: [_1]',
+ ''.
+ $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'').
+ '
';
+ next;
+ }
+ my $responsetype = $responseType->{$partid}->{$respid};
+ if (!exists($record->{"resource.$partid.$respid.submission"})) {
+ $lastsubonly.="\n".''.
+ ''.&mt('Part: [_1]',$display_part).''.
+ ' '.
+ '('.&mt('Response ID: [_1]',$respid).')'.
+ ' '.
+ ''.&mt('Nothing submitted - no attempts.').'
';
+ next;
+ }
+ foreach my $submission (@$string) {
+ my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
+ if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
+ my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4);
+ # Similarity check
+ my $similar='';
+ my ($type,$trial,$rndseed);
+ if ($hide eq 'rand') {
+ $type = 'randomizetry';
+ $trial = $record->{"resource.$partid.tries"};
+ $rndseed = $record->{"resource.$partid.rndseed"};
+ }
+ if ($env{'form.checkPlag'}) {
+ my ($oname,$odom,$ocrsid,$oessay,$osim)=
+ &most_similar($uname,$udom,$symb,$subval);
+ if ($osim) {
+ $osim=int($osim*100.0);
+ if ($hide eq 'anon') {
+ $similar='
'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'
'.
+ &mt('As the current submission is for an anonymous survey, no other details are available.').'
';
+ } else {
+ $similar='
';
+ if ($essayurl eq 'lib/templates/simpleproblem.problem') {
+ $similar .= ''.
+ &mt('Essay is [_1]% similar to an essay by [_2]',
+ $osim,
+ &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
+ '
';
+ } else {
+ my %old_course_desc;
+ if ($ocrsid ne '') {
+ if (ref($coursedesc_by_cid->{$ocrsid}) eq 'HASH') {
+ %old_course_desc = %{$coursedesc_by_cid->{$ocrsid}};
+ } else {
+ my $args;
+ if ($ocrsid ne $env{'request.course.id'}) {
+ $args = {'one_time' => 1};
+ }
+ %old_course_desc =
+ &Apache::lonnet::coursedescription($ocrsid,$args);
+ $coursedesc_by_cid->{$ocrsid} = \%old_course_desc;
+ }
+ $similar .=
+ ''.
+ &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',
+ $osim,
+ &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',
+ $old_course_desc{'description'},
+ $old_course_desc{'num'},
+ $old_course_desc{'domain'}).
+ '
';
+ } else {
+ $similar .=
+ ''.
+ &mt('Essay is [_1]% similar to an essay by [_2] in an unknown course',
+ $osim,
+ &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
+ '
';
+ }
+ }
+ $similar .= ''.
+ &keywords_highlight($oessay).
+ '
';
+ }
+ }
+ }
+ my $order=&get_order($partid,$respid,$symb,$uname,$udom,
+ undef,$type,$trial,$rndseed);
+ if (($viewtype eq 'lastonly') ||
+ ($viewtype eq 'datesub') ||
+ ($viewtype =~ /^(last|all)$/)) {
+ my $display_part=&get_display_part($partid,$symb);
+ $lastsubonly.=''.
+ '
'.&mt('Part: [_1]',$display_part).''.
+ '
'.
+ '('.&mt('Response ID: [_1]',$respid).')'.
+ ' ';
+ my $files=&get_submitted_files($udom,$uname,$partid,$respid,$record);
+ if (@$files) {
+ if ($hide eq 'anon') {
+ $lastsubonly.='
'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));
+ } else {
+ $lastsubonly.='
'.'
'.&mt('Submitted Files:').''
+ .'
';
+ if(@$files == 1) {
+ $lastsubonly .= &mt('Like all files provided by users, this file may contain viruses!');
+ } else {
+ $lastsubonly .= &mt('Like all files provided by users, these files may contain viruses!');
+ }
+ $lastsubonly .= '';
+ foreach my $file (@$files) {
+ &Apache::lonnet::allowuploaded('/adm/grades',$file);
+ $lastsubonly.='
'.$file.'';
+ }
+ }
+ $lastsubonly.='
';
+ }
+ if ($hide eq 'anon') {
+ $lastsubonly.='
'.&mt('Anonymous Survey').'';
+ } else {
+ $lastsubonly.='
'.&mt('Submitted Answer:').' ';
+ if ($draft) {
+ $lastsubonly.= '
'.&mt('Draft Copy').'';
+ }
+ $subval =
+ &cleanRecord($subval,$responsetype,$symb,$partid,
+ $respid,$record,$order,undef,$uname,$udom,$type,$trial,$rndseed);
+ if ($responsetype eq 'essay') {
+ $subval =~ s{\n}{
}g;
+ }
+ $lastsubonly.=$subval."\n";
+ }
+ if ($similar) {$lastsubonly.="
$similar\n";}
+ $lastsubonly.='
';
+ }
+ }
+ }
+ $lastsubonly.=''."\n"; # End: LC_grade_submissions_body
+ }
+ return ($lastsubonly,$partinfo);
+}
+
sub check_collaborators {
my ($symb,$uname,$udom,$record,$handgrade,$counter) = @_;
my ($result,@col_fullnames);
@@ -2500,18 +2925,46 @@ sub check_collaborators {
#--- Retrieve the last submission for all the parts
sub get_last_submission {
- my ($returnhash)=@_;
- my (@string,$timestamp,%lasthidden);
+ my ($returnhash,$is_tool)=@_;
+ my (@string,$timestamp,$lastgradetime,$lastsubmittime);
if ($$returnhash{'version'}) {
my %lasthash=();
- my ($version);
+ my %prevsolved=();
+ my %solved=();
+ my $version;
for ($version=1;$version<=$$returnhash{'version'};$version++) {
+ my %handgraded = ();
foreach my $key (sort(split(/\:/,
$$returnhash{$version.':keys'}))) {
$lasthash{$key}=$$returnhash{$version.':'.$key};
- $timestamp =
- &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});
+ if ($key =~ /\.([^.]+)\.regrader$/) {
+ $handgraded{$1} = 1;
+ } elsif ($key =~ /\.portfiles$/) {
+ if (($$returnhash{$version.':'.$key} ne '') &&
+ ($$returnhash{$version.':'.$key} !~ /\.\d+\.\w+$/)) {
+ $lastsubmittime = $$returnhash{$version.':timestamp'};
+ }
+ } elsif ($key =~ /\.submission$/) {
+ if ($$returnhash{$version.':'.$key} ne '') {
+ $lastsubmittime = $$returnhash{$version.':timestamp'};
+ }
+ } elsif ($key =~ /\.([^.]+)\.solved$/) {
+ $prevsolved{$1} = $solved{$1};
+ $solved{$1} = $lasthash{$key};
+ }
+ }
+ foreach my $partid (keys(%handgraded)) {
+ if (($prevsolved{$partid} eq 'ungraded_attempted') &&
+ (($solved{$partid} eq 'incorrect_by_override') ||
+ ($solved{$partid} eq 'correct_by_override'))) {
+ $lastgradetime = $$returnhash{$version.':timestamp'};
+ }
+ if ($solved{$partid} ne '') {
+ $prevsolved{$partid} = $solved{$partid};
+ }
}
+ $timestamp =
+ &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});
}
my (%typeparts,%randombytry);
my $showsurv =
@@ -2566,10 +3019,16 @@ sub get_last_submission {
}
}
if (!@string) {
+ my $msg;
+ if ($is_tool) {
+ $msg = &mt('No grade passed back.');
+ } else {
+ $msg = &mt('Nothing submitted - no attempts.');
+ }
$string[0] =
- '
'.&mt('Nothing submitted - no attempts.').'';
+ '
'.$msg.'';
}
- return (\@string,\$timestamp);
+ return (\@string,$timestamp,$lastgradetime,$lastsubmittime);
}
#--- High light keywords, with style choosen by user.
@@ -2776,13 +3235,31 @@ sub processHandGrade {
my $ntstu = $env{'form.NTSTU'};
my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my ($res_error,%queueable);
+ my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,\$res_error);
+ if ($res_error) {
+ $request->print(&navmap_errormsg());
+ return;
+ } else {
+ foreach my $part (@{$partlist}) {
+ if (ref($responseType->{$part}) eq 'HASH') {
+ foreach my $id (keys(%{$responseType->{$part}})) {
+ if (($responseType->{$part}->{$id} eq 'essay') ||
+ (lc($handgrade->{$part.'_'.$id}) eq 'yes')) {
+ $queueable{$part} = 1;
+ last;
+ }
+ }
+ }
+ }
+ }
if ($button eq 'Save & Next') {
my $ctr = 0;
while ($ctr < $ngrade) {
my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr});
my ($errorflag,$pts,$wgt,$numhidden) =
- &saveHandGrade($request,$symb,$uname,$udom,$ctr);
+ &saveHandGrade($request,$symb,$uname,$udom,$ctr,undef,undef,\%queueable);
if ($errorflag eq 'no_score') {
$ctr++;
next;
@@ -2837,7 +3314,7 @@ sub processHandGrade {
foreach my $collaborator (@collaborators) {
my ($errorflag,$pts,$wgt) =
&saveHandGrade($request,$symb,$collaborator,$udom,$ctr,
- $env{'form.unamedom'.$ctr},$part);
+ $env{'form.unamedom'.$ctr},$part,\%queueable);
if ($errorflag eq 'not_allowed') {
$request->print("
".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom")."");
next;
@@ -2859,13 +3336,12 @@ sub processHandGrade {
}
}
-# if ($env{'form.handgrade'} eq 'yes') {
- if (1) {
+ my %keyhash = ();
+ if ($numessay) {
# Keywords sorted in alphabatical order
my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
- my %keyhash = ();
$env{'form.keywords'} =~ s/,\s{0,}|\s+/ /g;
- $env{'form.keywords'} =~ s/^\s+|\s+$//;
+ $env{'form.keywords'} =~ s/^\s+|\s+$//g;
my (@keywords) = sort(split(/\s+/,$env{'form.keywords'}));
$env{'form.keywords'} = join(' ',@keywords);
$keyhash{$symb.'_keywords'} = $env{'form.keywords'};
@@ -2873,7 +3349,9 @@ sub processHandGrade {
$keyhash{$loginuser.'_kwclr'} = $env{'form.kwclr'};
$keyhash{$loginuser.'_kwsize'} = $env{'form.kwsize'};
$keyhash{$loginuser.'_kwstyle'} = $env{'form.kwstyle'};
+ }
+ if ($env{'form.compmsg'}) {
# message center - Order of message gets changed. Blank line is eliminated.
# New messages are saved in env for the next student.
# All messages are saved in nohist_handgrade.db
@@ -2888,17 +3366,20 @@ sub processHandGrade {
$ctr = 0;
while ($ctr < $ngrade) {
if ($env{'form.newmsg'.$ctr} ne '') {
- $keyhash{$symb.'_savemsg'.$idx} = $env{'form.newmsg'.$ctr};
- $env{'form.savemsg'.$idx} = $env{'form.newmsg'.$ctr};
- $idx++;
+ $keyhash{$symb.'_savemsg'.$idx} = $env{'form.newmsg'.$ctr};
+ $env{'form.savemsg'.$idx} = $env{'form.newmsg'.$ctr};
+ $idx++;
}
$ctr++;
}
$env{'form.savemsgN'} = --$idx;
$keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};
- my $putresult = &Apache::lonnet::put
- ('nohist_handgrade',\%keyhash,$cdom,$cnum);
}
+ if (($numessay) || ($env{'form.compmsg'})) {
+ my $putresult = &Apache::lonnet::put
+ ('nohist_handgrade',\%keyhash,$cdom,$cnum);
+ }
+
# Called by Save & Refresh from Highlight Attribute Window
my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');
if ($env{'form.refresh'} eq 'on') {
@@ -2938,7 +3419,6 @@ sub processHandGrade {
}
return $a cmp $b;
} (keys(%$fullname))) {
-# FIXME: this is fishy, looks like the button label
if ($nextflg == 1 && $button =~ /Next$/) {
push(@parsedlist,$item);
}
@@ -2949,14 +3429,7 @@ sub processHandGrade {
}
}
$ctr = 0;
-# FIXME: this is fishy, looks like the button label
@parsedlist = reverse @parsedlist if ($button eq 'Previous');
- my $res_error;
- my ($partlist) = &response_type($symb,\$res_error);
- if ($res_error) {
- $request->print(&navmap_errormsg());
- return;
- }
foreach my $student (@parsedlist) {
my $submitonly=$env{'form.submitonly'};
my ($uname,$udom) = split(/:/,$student);
@@ -3014,7 +3487,7 @@ sub processHandGrade {
#---- Save the score and award for each student, if changed
sub saveHandGrade {
- my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_;
+ my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part,$queueable) = @_;
my @version_parts;
my $usec = &Apache::lonnet::getsection($domain,$stuname,
$env{'request.course.id'});
@@ -3126,7 +3599,7 @@ sub saveHandGrade {
&Apache::lonnet::cstore(\%newrecord,$symb,
$env{'request.course.id'},$domain,$stuname);
&check_and_remove_from_queue(\@parts,\%record,\%newrecord,$symb,
- $cdom,$cnum,$domain,$stuname);
+ $cdom,$cnum,$domain,$stuname,$queueable);
}
if ($aggregateflag) {
&Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
@@ -3166,7 +3639,7 @@ sub makehidden {
}
sub check_and_remove_from_queue {
- my ($parts,$record,$newrecord,$symb,$cdom,$cnum,$domain,$stuname) = @_;
+ my ($parts,$record,$newrecord,$symb,$cdom,$cnum,$domain,$stuname,$queueable) = @_;
my @ungraded_parts;
foreach my $part (@{$parts}) {
if ( $record->{ 'resource.'.$part.'.awarded'} eq ''
@@ -3174,7 +3647,9 @@ sub check_and_remove_from_queue {
&& $newrecord->{'resource.'.$part.'.awarded'} eq ''
&& $newrecord->{'resource.'.$part.'.solved' } ne 'excused'
) {
- push(@ungraded_parts, $part);
+ if ($queueable->{$part}) {
+ push(@ungraded_parts, $part);
+ }
}
}
if ( !@ungraded_parts ) {
@@ -3209,7 +3684,7 @@ sub handback_files {
&Apache::lonnet::file_name_version_ext($answer_file);
my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);
my $getpropath = 1;
- my ($dir_list,$listerror) =
+ my ($dir_list,$listerror) =
&Apache::lonnet::dirlist($portfolio_root.$portfolio_path,
$domain,$stuname,$getpropath);
my $version = &Apache::lonnet::get_next_version($answer_name,$answer_ext,$dir_list);
@@ -3554,6 +4029,11 @@ VIEWJAVASCRIPT
#--- show scores for a section or whole class w/ option to change/update a score
sub viewgrades {
my ($request,$symb) = @_;
+ my ($is_tool,$toolsymb);
+ if ($symb =~ /ext\.tool$/) {
+ $is_tool = 1;
+ $toolsymb = $symb;
+ }
&viewgrades_js($request);
#need to make sure we have the correct data for later EXT calls,
@@ -3588,7 +4068,26 @@ sub viewgrades {
}
my ($common_header,$specific_header,@sections,$section_display);
- @sections = &Apache::loncommon::get_env_multiple('form.section');
+ if ($env{'request.course.sec'} ne '') {
+ @sections = ($env{'request.course.sec'});
+ } else {
+ @sections = &Apache::loncommon::get_env_multiple('form.section');
+ }
+
+# Check if Save button should be usable
+ my $disabled = ' disabled="disabled"';
+ if ($perm{'mgr'}) {
+ if (grep(/^all$/,@sections)) {
+ undef($disabled);
+ } else {
+ foreach my $sec (@sections) {
+ if (&canmodify($sec)) {
+ undef($disabled);
+ last;
+ }
+ }
+ }
+ }
if (grep(/^all$/,@sections)) {
@sections = ('all');
if ($group_display) {
@@ -3634,7 +4133,13 @@ sub viewgrades {
if ($env{'form.submitonly'} eq 'all') {
$result.= '
'.$common_header.'
';
} else {
- $result.= '
'.$common_header.' '.&mt('(submission status: "[_1]")',$submission_status).'
';
+ my $text;
+ if ($is_tool) {
+ $text = &mt('(transaction status: "[_1]")',$submission_status);
+ } else {
+ $text = &mt('(submission status: "[_1]")',$submission_status);
+ }
+ $result.= '
'.$common_header.' '.$text.'
';
}
$result .= &Apache::loncommon::start_data_table();
#radio buttons/text box for assigning points for a section or class.
@@ -3647,13 +4152,17 @@ sub viewgrades {
my %weight = ();
my $ctsparts = 0;
my %seen = ();
- my @part_response_id = &flatten_responseType($responseType);
+ my @part_response_id;
+ if ($is_tool) {
+ @part_response_id = ([0,'']);
+ } else {
+ @part_response_id = &flatten_responseType($responseType);
+ }
foreach my $part_response_id (@part_response_id) {
my ($partid,$respid) = @{ $part_response_id };
my $part_resp = join('_',@{ $part_response_id });
next if $seen{$partid};
$seen{$partid}++;
- my $handgrade=$$handgrade{$part_resp};
my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
$weight{$partid} = $wgt eq '' ? '1' : $wgt;
@@ -3702,7 +4211,13 @@ sub viewgrades {
if ($env{'form.submitonly'} eq 'all') {
$result.= '
'.$specific_header.'
';
} else {
- $result.= '
'.$specific_header.' '.&mt('(submission status: "[_1]")',$submission_status).'
';
+ my $text;
+ if ($is_tool) {
+ $text = &mt('(transaction status: "[_1]")',$submission_status);
+ } else {
+ $text = &mt('(submission status: "[_1]")',$submission_status);
+ }
+ $result.= '
'.$specific_header.' '.$text.'
';
}
$result.= &Apache::loncommon::start_data_table().
&Apache::loncommon::start_data_table_header_row().
@@ -3716,10 +4231,10 @@ sub viewgrades {
my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
my @partids = ();
foreach my $part (@parts) {
- my $display=&Apache::lonnet::metadata($url,$part.'.display');
+ my $display=&Apache::lonnet::metadata($url,$part.'.display',$toolsymb);
my $narrowtext = &mt('Tries');
$display =~ s|^Number of Attempts|$narrowtext
|; # makes the column narrower
- if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
+ if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name',$toolsymb); }
my ($partid) = &split_part_type($part);
push(@partids,$partid);
#
@@ -3760,11 +4275,11 @@ sub viewgrades {
return $a cmp $b;
} (keys(%$fullname))) {
$result.=&viewstudentgrade($symb,$env{'request.course.id'},
- $_,$$fullname{$_},\@parts,\%weight,\$ctr,\%last_resets);
+ $_,$$fullname{$_},\@parts,\%weight,\$ctr,\%last_resets,$is_tool);
}
$result.=&Apache::loncommon::end_data_table();
$result.='
'."\n";
- $result.='
'."\n";
if ($ctr == 0) {
my $stu_status = join(' or ',&Apache::loncommon::get_env_multiple('form.Status'));
@@ -3848,7 +4363,7 @@ sub viewgrades {
#--- call by previous routine to display each student who satisfies submission filter.
sub viewstudentgrade {
- my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_;
+ my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets,$is_tool) = @_;
my ($uname,$udom) = split(/:/,$student);
my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
my $submitonly = $env{'form.submitonly'};
@@ -3954,10 +4469,14 @@ sub viewstudentgrade {
# record does not get update if unchanged
sub editgrades {
my ($request,$symb) = @_;
+ my $toolsymb;
+ if ($symb =~ /ext\.tool$/) {
+ $toolsymb = $symb;
+ }
my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
my $title='
'.&mt('Current Grade Status').'
';
- $title.='
'.&mt('Section: [_1]',$section_display).'
'."\n";
+ $title.='
'.&mt('Section:').' '.$section_display.'
'."\n";
my $result= &Apache::loncommon::start_data_table().
&Apache::loncommon::start_data_table_header_row().
@@ -3991,6 +4510,7 @@ sub editgrades {
$ctr++;
}
my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
+ my $totcolspan = 0;
foreach my $partid (@partid) {
$header .= '
'.&mt('Old Score').' | '.
'
'.&mt('New Score').' | ';
@@ -3999,7 +4519,7 @@ sub editgrades {
my ($part,$type) = &split_part_type($stores);
if ($part !~ m/^\Q$partid\E/) { next;}
if ($type eq 'awarded' || $type eq 'solved') { next; }
- my $display=&Apache::lonnet::metadata($url,$stores.'.display');
+ my $display=&Apache::lonnet::metadata($url,$stores.'.display',$toolsymb);
$display =~ s/\[Part: \Q$part\E\]//;
my $narrowtext = &mt('Tries');
$display =~ s/Number of Attempts/$narrowtext/;
@@ -4007,6 +4527,7 @@ sub editgrades {
'
'.&mt('New').' '.$display.' | ';
$columns{$partid}+=2;
}
+ $totcolspan += $columns{$partid};
}
foreach my $partid (@partid) {
my $display_part=&get_display_part($partid,$symb);
@@ -4021,19 +4542,20 @@ sub editgrades {
&Apache::loncommon::end_data_table_header_row();
my @noupdate;
my ($updateCtr,$noupdateCtr) = (1,1);
+ my ($got_types,%queueable);
for ($i=0; $i<$env{'form.total'}; $i++) {
- my $line;
my $user = $env{'form.ctr'.$i};
my ($uname,$udom)=split(/:/,$user);
my %newrecord;
my $updateflag = 0;
- $line .= '
'.&nameUserString(undef,$$fullname{$user},$uname,$udom).' | ';
my $usec=$classlist->{"$uname:$udom"}[5];
- if (!&canmodify($usec)) {
- my $numcols=scalar(@partid)*4+2;
+ my $canmodify = &canmodify($usec);
+ my $line = '
'.
+ &nameUserString(undef,$$fullname{$user},$uname,$udom).' | ';
+ if (!$canmodify) {
push(@noupdate,
- $line."
".
- &mt('Not allowed to modify student')." | ");
+ $line."
".
+ &mt('Not allowed to modify student')." | ");
next;
}
my %aggregate = ();
@@ -4120,14 +4642,33 @@ sub editgrades {
$udom,$uname);
my $all_graded = 1;
my $none_graded = 1;
+ unless ($got_types) {
+ my $error;
+ my ($plist,$handgrd,$resptype) = &response_type($symb,\$error);
+ unless ($error) {
+ foreach my $part (@parts) {
+ if (ref($resptype->{$part}) eq 'HASH') {
+ foreach my $id (keys(%{$resptype->{$part}})) {
+ if (($resptype->{$part}->{$id} eq 'essay') ||
+ (lc($handgrd->{$part.'_'.$id}) eq 'yes')) {
+ $queueable{$part} = 1;
+ last;
+ }
+ }
+ }
+ }
+ }
+ $got_types = 1;
+ }
foreach my $part (@parts) {
- if ( $record{'resource.'.$part.'.awarded'} eq '' ) {
- $all_graded = 0;
- } else {
- $none_graded = 0;
+ if ($queueable{$part}) {
+ if ( $record{'resource.'.$part.'.awarded'} eq '' ) {
+ $all_graded = 0;
+ } else {
+ $none_graded = 0;
+ }
}
- }
-
+ }
if ($all_graded || $none_graded) {
&Apache::bridgetask::remove_from_queue('gradingqueue',
$symb,$cdom,$cnum,
@@ -4150,8 +4691,7 @@ sub editgrades {
}
}
if (@noupdate) {
-# my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;
- my $numcols=scalar(@partid)*4+2;
+ my $numcols=$totcolspan+2;
$result .= &Apache::loncommon::start_data_table_row('LC_empty_row').
'
'.
&mt('No Changes Occurred For the Students Below').
@@ -4192,7 +4732,7 @@ sub split_part_type {
#
#--- Javascript to handle csv upload
sub csvupload_javascript_reverse_associate {
- my $error1=&mt('You need to specify the username or the student/employee ID');
+ my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
my $error2=&mt('You need to specify at least one grading field');
&js_escape(\$error1);
&js_escape(\$error2);
@@ -4201,13 +4741,15 @@ sub csvupload_javascript_reverse_associa
var foundsomething=0;
var founduname=0;
var foundID=0;
+ var foundclicker=0;
for (i=0;i<=vf.nfields.value;i++) {
tw=eval('vf.f'+i+'.selectedIndex');
if (i==0 && tw!=0) { foundID=1; }
if (i==1 && tw!=0) { founduname=1; }
- if (i!=0 && i!=1 && i!=2 && tw!=0) { foundsomething=1; }
+ if (i==2 && tw!=0) { foundclicker=1; }
+ if (i!=0 && i!=1 && i!=2 && i!=3 && tw!=0) { foundsomething=1; }
}
- if (founduname==0 && foundID==0) {
+ if (founduname==0 && foundID==0 && foundclicker==0) {
alert('$error1');
return;
}
@@ -4234,7 +4776,7 @@ ENDPICK
}
sub csvupload_javascript_forward_associate {
- my $error1=&mt('You need to specify the username or the student/employee ID');
+ my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
my $error2=&mt('You need to specify at least one grading field');
&js_escape(\$error1);
&js_escape(\$error2);
@@ -4243,13 +4785,15 @@ sub csvupload_javascript_forward_associa
var foundsomething=0;
var founduname=0;
var foundID=0;
+ var foundclicker=0;
for (i=0;i<=vf.nfields.value;i++) {
tw=eval('vf.f'+i+'.selectedIndex');
if (tw==1) { foundID=1; }
if (tw==2) { founduname=1; }
- if (tw>3) { foundsomething=1; }
+ if (tw==3) { foundclicker=1; }
+ if (tw>4) { foundsomething=1; }
}
- if (founduname==0 && foundID==0) {
+ if (founduname==0 && foundID==0 && Æ’oundclicker==0) {
alert('$error1');
return;
}
@@ -4307,6 +4851,10 @@ ENDPICK
sub csvupload_fields {
my ($symb,$errorref) = @_;
+ my $toolsymb;
+ if ($symb =~ /ext\.tool$/) {
+ $toolsymb = $symb;
+ }
my (@parts) = &getpartlist($symb,$errorref);
if (ref($errorref)) {
if ($$errorref) {
@@ -4315,15 +4863,15 @@ sub csvupload_fields {
}
my @fields=(['ID','Student/Employee ID'],
- ['clicker','Clicker ID'],
['username','Student Username'],
+ ['clicker','Clicker ID'],
['domain','Student Domain']);
my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
foreach my $part (sort(@parts)) {
my @datum;
- my $display=&Apache::lonnet::metadata($url,$part.'.display');
+ my $display=&Apache::lonnet::metadata($url,$part.'.display',$toolsymb);
my $name=$part;
- if (!$display) { $display = $name; }
+ if (!$display) { $display = $name; }
@datum=($name,$display);
if ($name=~/^stores_(.*)_awarded/) {
push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);
@@ -4391,15 +4939,17 @@ ENDUPFORM
sub csvuploadmap {
- my ($request,$symb)= @_;
+ my ($request,$symb) = @_;
if (!$symb) {return '';}
my $datatoken;
if (!$env{'form.datatoken'}) {
$datatoken=&Apache::loncommon::upfile_store($request);
} else {
- $datatoken=$env{'form.datatoken'};
- &Apache::loncommon::load_tmp_file($request);
+ $datatoken=&Apache::loncommon::valid_datatoken($env{'form.datatoken'});
+ if ($datatoken ne '') {
+ &Apache::loncommon::load_tmp_file($request,$datatoken);
+ }
}
my @records=&Apache::loncommon::upfile_record_sep();
&csvuploadmap_header($request,$symb,$datatoken,$#records+1);
@@ -4485,10 +5035,13 @@ sub get_fields {
}
sub csvuploadassign {
- my ($request,$symb)= @_;
+ my ($request,$symb) = @_;
if (!$symb) {return '';}
my $error_msg = '';
- &Apache::loncommon::load_tmp_file($request);
+ my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'});
+ if ($datatoken ne '') {
+ &Apache::loncommon::load_tmp_file($request,$datatoken);
+ }
my @gradedata = &Apache::loncommon::upfile_record_sep();
my %fields=&get_fields();
my $courseid=$env{'request.course.id'};
@@ -4598,7 +5151,7 @@ sub csvuploadassign {
$grades{$store_key}=$entries{$fields{$dest}};
}
}
- if (! %grades) {
+ if (! %grades) {
push(@skipped,&mt("[_1]: no data to save","$username:$domain"));
} else {
$grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
@@ -4669,6 +5222,7 @@ LISTJAVASCRIPT
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 $getgroup = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
my $result=' '.
&mt('Manual Grading by Page or Sequence').'';
@@ -4758,7 +5312,7 @@ LISTJAVASCRIPT
' | '.&nameUserString('header').' | '.
&Apache::loncommon::end_data_table_header_row();
- my (undef,undef,$fullname) = &getclasslist($getsec,'1');
+ my (undef,undef,$fullname) = &getclasslist($getsec,'1',$getgroup);
my $ptr = 1;
foreach my $student (sort
{
@@ -4808,7 +5362,7 @@ sub getSymbMap {
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) ) {
+ if ($navmap->hasResource($sequence, sub { shift->is_gradable(); }, 0) ) {
my $title = $minder.'.'.
&HTML::Entities::encode($sequence->compTitle(),'"\'&');
push(@titles, $title); # minder in case two titles are identical
@@ -4905,10 +5459,11 @@ sub displayPage {
if($curRes == $iterator->BEGIN_MAP) { $depth++; }
if($curRes == $iterator->END_MAP) { $depth--; }
- if (ref($curRes) && $curRes->is_problem()) {
+ if (ref($curRes) && $curRes->is_gradable()) {
my $parts = $curRes->parts();
my $title = $curRes->compTitle();
my $symbx = $curRes->symb();
+ my $is_tool = ($symbx =~ /ext\.tool$/);
$studentTable.=
&Apache::loncommon::start_data_table_row().
'
'.$prob.
@@ -4919,26 +5474,34 @@ sub displayPage {
' | ';
$studentTable.='
';
my %form = ('CODE' => $env{'form.CODE'},);
- if ($env{'form.vProb'} eq 'yes' ) {
- $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
- undef,'both',\%form);
- } else {
- my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);
- $companswer =~ s|||g;
-# while ($companswer =~ /()/s) { #\n");
-# }
-# $companswer =~ s|||g;
- $studentTable.=' '.$title.' '.&mt('Correct answer').': '.$companswer;
+ if ($is_tool) {
+ $studentTable.=' '.$title.' ';
+ } else {
+ if ($env{'form.vProb'} eq 'yes' ) {
+ $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
+ undef,'both',\%form);
+ } else {
+ my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);
+ $companswer =~ s|||g;
+# while ($companswer =~ /()/s) { #\n");
+# }
+# $companswer =~ s|||g;
+ $studentTable.=' '.$title.' '.&mt('Correct answer').': '.$companswer;
+ }
}
my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
if ($env{'form.lastSub'} eq 'datesub') {
if ($record{'version'} eq '') {
- $studentTable.=' '.&mt('No recorded submission for this problem.').' ';
+ my $msg = &mt('No recorded submission for this problem.');
+ if ($is_tool) {
+ $msg = &mt('No recorded transactions for this external tool');
+ }
+ $studentTable.=' '.$msg.' ';
} else {
my %responseType = ();
foreach my $partid (@{$parts}) {
@@ -4951,7 +5514,6 @@ sub displayPage {
$responseType{$partid} = \%responseIds;
}
$studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);
-
}
} elsif ($env{'form.lastSub'} eq 'all') {
my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
@@ -4977,10 +5539,14 @@ sub displayPage {
}
$curRes = $iterator->next();
}
+ my $disabled;
+ unless (&canmodify($usec)) {
+ $disabled = ' disabled="disabled"';
+ }
$studentTable.=
' '."\n".
- ''.
''."\n";
$request->print($studentTable);
@@ -4992,13 +5558,14 @@ sub displaySubByDates {
my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;
my $isCODE=0;
my $isTask = ($symb =~/\.task$/);
+ my $is_tool = ($symb =~/\.tool$/);
if (exists($record->{'resource.CODE'})) { $isCODE=1; }
my $studentTable=&Apache::loncommon::start_data_table().
&Apache::loncommon::start_data_table_header_row().
''.&mt('Date/Time').' | '.
($isCODE?''.&mt('CODE').' | ':'').
($isTask?''.&mt('Version').' | ':'').
- ''.&mt('Submission').' | '.
+ ''.($is_tool?&mt('Grade'):&mt('Submission')).' | '.
''.&mt('Status').' | '.
&Apache::loncommon::end_data_table_header_row();
my ($version);
@@ -5006,7 +5573,11 @@ sub displaySubByDates {
my %orders;
$mark{'correct_by_student'} = $checkIcon;
if (!exists($$record{'1:timestamp'})) {
- return ' '.&mt('Nothing submitted - no attempts.').' ';
+ if ($is_tool) {
+ return ' '.&mt('No grade passed back.').' ';
+ } else {
+ return ' '.&mt('Nothing submitted - no attempts.').' ';
+ }
}
my $interaction;
@@ -5039,56 +5610,64 @@ sub displaySubByDates {
if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {
$hidden = 1;
}
- my @matchKey = ($isTask ? sort(grep /^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys)
- : sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));
-
+ my @matchKey;
+ if ($isTask) {
+ @matchKey = sort(grep(/^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys));
+ } elsif ($is_tool) {
+ @matchKey = sort(grep(/^resource\.\Q$partid\E\.awarded$/,@versionKeys));
+ } else {
+ @matchKey = sort(grep(/^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));
+ }
# next if ($$record{"$version:resource.$partid.solved"} eq '');
my $display_part=&get_display_part($partid,$symb);
foreach my $matchKey (@matchKey) {
if (exists($$record{$version.':'.$matchKey}) &&
$$record{$version.':'.$matchKey} ne '') {
-
- my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
- : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
- $displaySub[0].='';
- $displaySub[0].=''.&mt('Part: [_1]',$display_part).''
- .' '
- .'('.&mt('Response ID: [_1]',$responseId).')'
- .''
- .' ';
- if ($hidden) {
- $displaySub[0].= &mt('Anonymous Survey').'';
+ if ($is_tool) {
+ $displaySub[0].=$$record{"$version:resource.$partid.awarded"};
} else {
- my ($trial,$rndseed,$newvariation);
- if ($type eq 'randomizetry') {
- $trial = $$record{"$where.$partid.tries"};
- $rndseed = $$record{"$where.$partid.rndseed"};
- }
- if ($$record{"$where.$partid.tries"} eq '') {
- $displaySub[0].=&mt('Trial not counted');
- } else {
- $displaySub[0].=&mt('Trial: [_1]',
- $$record{"$where.$partid.tries"});
- if (($rndseed ne '') && ($lastrndseed{$partid} ne '')) {
- if (($rndseed ne $lastrndseed{$partid}) &&
- (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) {
- $newvariation = ' ('.&mt('New variation this try').')';
- }
+ my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
+ : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
+ $displaySub[0].='';
+ $displaySub[0].=''.&mt('Part: [_1]',$display_part).''
+ .' '
+ .'('.&mt('Response ID: [_1]',$responseId).')'
+ .''
+ .' ';
+ if ($hidden) {
+ $displaySub[0].= &mt('Anonymous Survey').'';
+ } else {
+ my ($trial,$rndseed,$newvariation);
+ if ($type eq 'randomizetry') {
+ $trial = $$record{"$where.$partid.tries"};
+ $rndseed = $$record{"$where.$partid.rndseed"};
}
- $lastrndseed{$partid} = $rndseed;
- $lasttype{$partid} = $type;
- }
- my $responseType=($isTask ? 'Task'
+ if ($$record{"$where.$partid.tries"} eq '') {
+ $displaySub[0].=&mt('Trial not counted');
+ } else {
+ $displaySub[0].=&mt('Trial: [_1]',
+ $$record{"$where.$partid.tries"});
+ if (($rndseed ne '') && ($lastrndseed{$partid} ne '')) {
+ if (($rndseed ne $lastrndseed{$partid}) &&
+ (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) {
+ $newvariation = ' ('.&mt('New variation this try').')';
+ }
+ }
+ $lastrndseed{$partid} = $rndseed;
+ $lasttype{$partid} = $type;
+ }
+ my $responseType=($isTask ? 'Task'
: $responseType->{$partid}->{$responseId});
- if (!exists($orders{$partid})) { $orders{$partid}={}; }
- if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {
- $orders{$partid}->{$responseId}=
- &get_order($partid,$responseId,$symb,$uname,$udom,
- $no_increment,$type,$trial,$rndseed);
- }
- $displaySub[0].=''.$newvariation.''; # /nobreak
- $displaySub[0].=' '.
- &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).' ';
+ if (!exists($orders{$partid})) { $orders{$partid}={}; }
+ if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {
+ $orders{$partid}->{$responseId}=
+ &get_order($partid,$responseId,$symb,$uname,$udom,
+ $no_increment,$type,$trial,$rndseed);
+ }
+ $displaySub[0].=''.$newvariation.''; # /nobreak
+ $displaySub[0].=' '.
+ &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).' ';
+ }
}
}
}
@@ -5103,14 +5682,22 @@ sub displaySubByDates {
lc($$record{"$where.$partid.award"}).' '.
$mark{$$record{"$where.$partid.solved"}}.
' ';
+ } elsif (($is_tool) && (exists($$record{"$version:resource.$partid.solved"}))) {
+ if ($$record{"$version:resource.$partid.solved"} =~ /^(in|)correct_by_passback$/) {
+ $displaySub[1].=&mt('Grade passed back by external tool');
+ }
}
if (exists $$record{"$where.$partid.regrader"}) {
- $displaySub[2].=$$record{"$where.$partid.regrader"}.
- ' ('.&mt('Part').': '.$display_part.')';
+ $displaySub[2].=$$record{"$where.$partid.regrader"};
+ unless ($is_tool) {
+ $displaySub[2].=' ('.&mt('Part').': '.$display_part.')';
+ }
} elsif ($$record{"$version:resource.$partid.regrader"} =~ /\S/) {
$displaySub[2].=
- $$record{"$version:resource.$partid.regrader"}.
- ' ('.&mt('Part').': '.$display_part.')';
+ $$record{"$version:resource.$partid.regrader"};
+ unless ($is_tool) {
+ $displaySub[2].=' ('.&mt('Part').': '.$display_part.')';
+ }
}
}
# needed because old essay regrader has not parts info
@@ -5195,6 +5782,7 @@ sub updateGradeByPage {
my @displayPts=();
my %aggregate = ();
my $aggregateflag = 0;
+ my %queueable;
if ($env{'form.HIDE'.$prob}) {
my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
my ($version,$parts) = split(/:/,$env{'form.HIDE'.$prob},2);
@@ -5204,7 +5792,20 @@ sub updateGradeByPage {
foreach my $partid (@{$parts}) {
my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid};
my $oldpts = $env{'form.oldpts'.$question.'_'.$partid};
-
+ my @types = $curRes->responseType($partid);
+ if (grep(/^essay$/,@types)) {
+ $queueable{$partid} = 1;
+ } else {
+ my @ids = $curRes->responseIds($partid);
+ for (my $i=0; $i < scalar(@ids); $i++) {
+ my $hndgrd = &Apache::lonnet::EXT('resource.'.$partid.'_'.$ids[$i].
+ '.handgrade',$symb);
+ if (lc($hndgrd) eq 'yes') {
+ $queueable{$partid} = 1;
+ last;
+ }
+ }
+ }
my $wgt = $env{'form.WGT'.$question.'_'.$partid} != 0 ?
$env{'form.WGT'.$question.'_'.$partid} : 1;
my $partial = $newpts/$wgt;
@@ -5270,7 +5871,7 @@ sub updateGradeByPage {
$env{'request.course.id'},
$udom,$uname);
&check_and_remove_from_queue($parts,\%record,undef,$symbx,
- $cdom,$cnum,$udom,$uname);
+ $cdom,$cnum,$udom,$uname,\%queueable);
}
if ($aggregateflag) {
@@ -5369,7 +5970,7 @@ the homework problem.
sub defaultFormData {
my ($symb)=@_;
- return '';
+ return '';
}
@@ -5522,7 +6123,7 @@ sub scantron_uploads {
sub scantron_scantab {
my $result='
'.&mt("If this information is correct, please click on '[_1]'.",&mt($button_text)).'
'.&mt('If something is incorrect, please return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','','').'
-
-
-');
+';
}
=pod
@@ -6874,15 +7344,58 @@ sub scantron_do_warning {
}
if ( $env{'form.scantron_selectfile'} eq '') {
$r->print(''.&mt("You have not selected a file that contains the student's response data.").' ');
- }
+ }
if ( $env{'form.scantron_format'} eq '') {
$r->print(''.&mt("You have not selected the format of the student's response data.").' ');
- }
+ }
} else {
my $warning=&scantron_warning_screen('Grading: Validate Records',$symb);
+ my ($checksec,@possibles) = &gradable_sections();
+ my $gradesections;
+ if ($checksec) {
+ my $file=$env{'form.scantron_selectfile'};
+ if (&valid_file($file)) {
+ my %bysec = &scantron_get_sections();
+ my $table;
+ if ((keys(%bysec) > 1) || ((keys(%bysec) == 1) && ((keys(%bysec))[0] ne $checksec))) {
+ $gradesections = &mt('Your current role is for section [_1].',''.$checksec.'').' ';
+ $table = &Apache::loncommon::start_data_table()."\n".
+ &Apache::loncommon::start_data_table_header_row().
+ ''.&mt('Section').' | '.&mt('Number of records').' | '.
+ &Apache::loncommon::end_data_table_header_row()."\n";
+ if ($bysec{'none'}) {
+ $table .= &Apache::loncommon::start_data_table_row().
+ ''.&mt('None').' | '.$bysec{'none'}.' | '.
+ &Apache::loncommon::end_data_table_row()."\n";
+ }
+ foreach my $sec (sort { $a <=> $b } keys(%bysec)) {
+ next if ($sec eq 'none');
+ $table .= &Apache::loncommon::start_data_table_row().
+ ''.$sec.' | '.$bysec{$sec}.' | '.
+ &Apache::loncommon::end_data_table_row()."\n";
+ }
+ $table .= &Apache::loncommon::end_data_table()."\n";
+ $gradesections .= &mt('Sections represented in the bubblesheet data file (based on bubbled student IDs) are as follows:').
+ ''.$table.' ';
+ if (@possibles) {
+ $gradesections .= ''.
+ &mt('You have role(s) in [quant,_1,other section,other sections] with privileges to manage grades.',
+ scalar(@possibles)).' '.
+ &mt('Check which of those section(s), in addition to section [_1], you wish to grade using this bubblesheet file:',
+ ''.$checksec.'').' ';
+ foreach my $sec (sort {$a <=> $b } @possibles) {
+ $gradesections .= ''.(' 'x2);
+ }
+ $gradesections .= ' ';
+ }
+ }
+ } else {
+ $gradesections = ''.&mt('The selected file is unavailable').' ';
+ }
+ }
my $bubbledbyhand=&hand_bubble_option();
$r->print('
-'.$warning.$bubbledbyhand.'
+'.$warning.$gradesections.$bubbledbyhand.'
');
@@ -6969,11 +7482,42 @@ sub scantron_validate_file {
if ($env{'form.scantron_corrections'}) {
&scantron_process_corrections($r);
}
- $r->print(''.&mt('Gathering necessary information.').' ');$r->rflush();
+
+ $r->print(''.&mt('Gathering necessary information.').' ');
+ my ($checksec,@gradable);
+ if ($env{'request.course.sec'}) {
+ ($checksec,my @possibles) = &gradable_sections();
+ if ($checksec) {
+ if (@possibles) {
+ my @chosensecs = &Apache::loncommon::get_env_multiple('form.scantron_othersections');
+ if (@chosensecs) {
+ foreach my $sec (@chosensecs) {
+ if (grep(/^\Q$sec\E$/,@possibles)) {
+ unless (grep(/^\Q$sec\E$/,@gradable)) {
+ push(@gradable,$sec);
+ }
+ }
+ }
+ }
+ }
+ $r->print('');
+ if (@gradable) {
+ my @showsections = sort { $a <=> $b } (@gradable,$checksec);
+ $r->print(
+ ''.&mt('Sections to be Graded:').' | '.join(', ',@showsections).' | ');
+ } else {
+ $r->print(
+ ''.&mt('Section to be Graded:').' | '.$checksec.' | ');
+ }
+ $r->print(' ');
+ }
+ }
+ $r->rflush();
+
#get the student pick code ready
$r->print(&Apache::loncommon::studentbrowser_javascript());
my $nav_error;
- my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
+ my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);
if ($nav_error) {
$r->print(&navmap_errormsg());
@@ -6994,7 +7538,7 @@ sub scantron_validate_file {
$env{'form.validatepass'} = 0;
}
my $currentphase=$env{'form.validatepass'};
-
+ my %skipbysec=();
my $stop=0;
while (!$stop && $currentphase < scalar(@validate_phases)) {
@@ -7004,13 +7548,29 @@ sub scantron_validate_file {
my $which="scantron_validate_".$validate_phases[$currentphase];
{
no strict 'refs';
- ($stop,$currentphase)=&$which($r,$currentphase);
+ my @extras=();
+ if ($validate_phases[$currentphase] eq 'ID') {
+ @extras = (\%skipbysec,$checksec,@gradable);
+ }
+ ($stop,$currentphase)=&$which($r,$currentphase,@extras);
}
}
if (!$stop) {
my $warning=&scantron_warning_screen('Start Grading',$symb);
+ my $secinfo;
+ if (keys(%skipbysec) > 0) {
+ my $seclist = '';
+ foreach my $sec (sort { $a <=> $b } keys(%skipbysec)) {
+ $seclist .= '- '.&mt('section [_1]: [_2]',$sec,$skipbysec{$sec}).'
';
+ }
+ $seclist .= ' ';
+ $secinfo = ''.
+ &mt('Numbers of records for students in sections not being graded [_1]',
+ $seclist).
+ ' ';
+ }
$r->print(&mt('Validation process complete.').' '.
- $warning.
+ $secinfo.$warning.
&mt('Perform verification for each student after storage of submissions?').
' '.
@@ -7426,14 +7986,15 @@ sub scantron_validate_sequence {
sub scantron_validate_ID {
- my ($r,$currentphase) = @_;
+ my ($r,$currentphase,$skipbysec,$checksec,@gradable) = @_;
#get student info
my $classlist=&Apache::loncoursedata::get_classlist();
my %idmap=&username_to_idmap($classlist);
+ my $secidx = &Apache::loncoursedata::CL_SECTION();
#get scantron line setup
- my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
+ my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
my ($scanlines,$scan_data)=&scantron_getfile();
my $nav_error;
@@ -7444,6 +8005,7 @@ sub scantron_validate_ID {
}
my %found=('ids'=>{},'usernames'=>{});
+ my $unsavedskips = 0;
for (my $i=0;$i<=$scanlines->{'count'};$i++) {
my $line=&scantron_get_line($scanlines,$scan_data,$i);
if ($line=~/^[\s\cz]*$/) { next; }
@@ -7456,13 +8018,41 @@ sub scantron_validate_ID {
}
if ($found) {
my $username=$idmap{$found};
+ if ($checksec) {
+ if (ref($classlist->{$username}) eq 'ARRAY') {
+ my $stusec = $classlist->{$username}->[$secidx];
+ if ($stusec ne $checksec) {
+ unless ((@gradable > 0) && (grep(/^\Q$stusec\E$/,@gradable))) {
+ my $skip=1;
+ &scantron_put_line($scanlines,$scan_data,$i,$line,$skip);
+ if (ref($skipbysec) eq 'HASH') {
+ if ($stusec eq '') {
+ $skipbysec->{'none'} ++;
+ } else {
+ $skipbysec->{$stusec} ++;
+ }
+ }
+ $unsavedskips ++;
+ next;
+ }
+ }
+ }
+ }
if ($found{'ids'}{$found}) {
&scantron_get_correction($r,$i,$scan_record,\%scantron_config,
$line,'duplicateID',$found);
+ if ($unsavedskips) {
+ &scantron_putfile($scanlines,$scan_data);
+ $unsavedskips = 0;
+ }
return(1,$currentphase);
} elsif ($found{'usernames'}{$username}) {
&scantron_get_correction($r,$i,$scan_record,\%scantron_config,
$line,'duplicateID',$username);
+ if ($unsavedskips) {
+ &scantron_putfile($scanlines,$scan_data);
+ $unsavedskips = 0;
+ }
return(1,$currentphase);
}
#FIXME store away line we previously saw the ID on to use above
@@ -7471,29 +8061,95 @@ sub scantron_validate_ID {
} else {
if ($id =~ /^\s*$/) {
my $username=&scan_data($scan_data,"$i.user");
- if (defined($username) && $found{'usernames'}{$username}) {
+ if (($checksec && $username ne '')) {
+ if (ref($classlist->{$username}) eq 'ARRAY') {
+ my $stusec = $classlist->{$username}->[$secidx];
+ if ($stusec ne $checksec) {
+ unless ((@gradable > 0) && (grep(/^\Q$stusec\E$/,@gradable))) {
+ my $skip=1;
+ &scantron_put_line($scanlines,$scan_data,$i,$line,$skip);
+ if (ref($skipbysec) eq 'HASH') {
+ if ($stusec eq '') {
+ $skipbysec->{'none'} ++;
+ } else {
+ $skipbysec->{$stusec} ++;
+ }
+ }
+ $unsavedskips ++;
+ next;
+ }
+ }
+ }
+ } elsif (defined($username) && $found{'usernames'}{$username}) {
&scantron_get_correction($r,$i,$scan_record,
\%scantron_config,
$line,'duplicateID',$username);
+ if ($unsavedskips) {
+ &scantron_putfile($scanlines,$scan_data);
+ $unsavedskips = 0;
+ }
return(1,$currentphase);
} elsif (!defined($username)) {
&scantron_get_correction($r,$i,$scan_record,
\%scantron_config,
$line,'incorrectID');
+ if ($unsavedskips) {
+ &scantron_putfile($scanlines,$scan_data);
+ $unsavedskips = 0;
+ }
return(1,$currentphase);
}
$found{'usernames'}{$username}++;
} else {
&scantron_get_correction($r,$i,$scan_record,\%scantron_config,
$line,'incorrectID');
+ if ($unsavedskips) {
+ &scantron_putfile($scanlines,$scan_data);
+ $unsavedskips = 0;
+ }
return(1,$currentphase);
}
}
}
-
+ if ($unsavedskips) {
+ &scantron_putfile($scanlines,$scan_data);
+ $unsavedskips = 0;
+ }
return (0,$currentphase+1);
}
+sub scantron_get_sections {
+ my %bysec;
+ if ($env{'form.scantron_format'} ne '') {
+ my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
+ my ($scanlines,$scan_data)=&scantron_getfile();
+ my $classlist=&Apache::loncoursedata::get_classlist();
+ my %idmap=&username_to_idmap($classlist);
+ foreach my $key (keys(%idmap)) {
+ my $lckey = lc($key);
+ $idmap{$lckey} = $idmap{$key};
+ }
+ my $secidx = &Apache::loncoursedata::CL_SECTION();
+ for (my $i=0;$i<=$scanlines->{'count'};$i++) {
+ my $line=&scantron_get_line($scanlines,$scan_data,$i);
+ if ($line=~/^[\s\cz]*$/) { next; }
+ my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
+ $scan_data);
+ my $id=lc($$scan_record{'scantron.ID'});
+ if (exists($idmap{$id})) {
+ if (ref($classlist->{$idmap{$id}}) eq 'ARRAY') {
+ my $stusec = $classlist->{$idmap{$id}}->[$secidx];
+ if ($stusec eq '') {
+ $bysec{'none'} ++;
+ } else {
+ $bysec{$stusec} ++;
+ }
+ }
+ }
+ }
+ }
+ return %bysec;
+}
sub scantron_get_correction {
my ($r,$i,$scan_record,$scan_config,$line,$error,$arg,
@@ -7680,7 +8336,7 @@ sub verify_bubbles_checked {
my $ansnumstr = join('","',@ansnums);
my $warning = &mt("A bubble or 'No bubble' selection has not been made for one or more lines.");
&js_escape(\$warning);
- my $output = &Apache::lonhtmlcommon::scripttag((<new();
@@ -8159,6 +8815,17 @@ sub scantron_validate_doublebubble {
if (ref($map)) {
$randomorder = $map->randomorder();
$randompick = $map->randompick();
+ unless ($randomorder || $randompick) {
+ foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
+ if ($res->randomorder()) {
+ $randomorder = 1;
+ }
+ if ($res->randompick()) {
+ $randompick = 1;
+ }
+ last if ($randomorder || $randompick);
+ }
+ }
if ($randomorder || $randompick) {
$nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
if ($nav_error) {
@@ -8323,7 +8990,7 @@ sub scantron_validate_missingbubbles {
&Apache::lonnet::decode_symb($env{'form.selectpage'});
#get scantron line setup
- my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
+ my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
my ($scanlines,$scan_data)=&scantron_getfile();
my $navmap = Apache::lonnavmaps::navmap->new();
@@ -8342,6 +9009,17 @@ sub scantron_validate_missingbubbles {
if (ref($map)) {
$randomorder = $map->randomorder();
$randompick = $map->randompick();
+ unless ($randomorder || $randompick) {
+ foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
+ if ($res->randomorder()) {
+ $randomorder = 1;
+ }
+ if ($res->randompick()) {
+ $randompick = 1;
+ }
+ last if ($randomorder || $randompick);
+ }
+ }
if ($randomorder || $randompick) {
$nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
if ($nav_error) {
@@ -8452,7 +9130,7 @@ sub hand_bubble_option {
}
}
if ($needs_hand_bubbles) {
- my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
+ my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
return &mt('The sequence to be graded contains response types which are handgraded.').''.
&mt('If you have already graded these by bubbling sheets to indicate points awarded, [_1]what point value is assigned to a filled last bubble in each row?',' ').
@@ -8471,7 +9149,7 @@ sub scantron_process_students {
}
my $default_form_data=&defaultFormData($symb);
- my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
+ my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
my ($scanlines,$scan_data)=&scantron_getfile();
my $classlist=&Apache::loncoursedata::get_classlist();
@@ -8483,10 +9161,21 @@ sub scantron_process_students {
}
my $map=$navmap->getResourceByUrl($sequence);
my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
- %grader_randomlists_by_symb);
+ %grader_randomlists_by_symb,%symb_for_examcode);
if (ref($map)) {
$randomorder = $map->randomorder();
$randompick = $map->randompick();
+ unless ($randomorder || $randompick) {
+ foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
+ if ($res->randomorder()) {
+ $randomorder = 1;
+ }
+ if ($res->randompick()) {
+ $randompick = 1;
+ }
+ last if ($randomorder || $randompick);
+ }
+ }
} else {
$r->print(&navmap_errormsg());
return '';
@@ -8494,7 +9183,7 @@ sub scantron_process_students {
my $nav_error;
my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
if ($randomorder || $randompick) {
- $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
+ $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource,1,\%symb_for_examcode);
if ($nav_error) {
$r->print(&navmap_errormsg());
return '';
@@ -8511,9 +9200,10 @@ sub scantron_process_students {
SCANTRONFORM
$r->print($result);
+ my ($checksec,@possibles)=&gradable_sections();
my @delayqueue;
my (%completedstudents,%scandata);
-
+
my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));
my $count=&get_todo_count($scanlines,$scan_data);
my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);
@@ -8539,7 +9229,7 @@ SCANTRONFORM
return ''; # Dunno why the other returns return '' rather than just returning.
}
- my %lettdig = &letter_to_digits();
+ my %lettdig = &Apache::lonnet::letter_to_digits();
my $numletts = scalar(keys(%lettdig));
my %orderedforcode;
@@ -8573,6 +9263,13 @@ SCANTRONFORM
next;
}
my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION];
+ if (($checksec ne '') && ($checksec ne $usec)) {
+ unless (grep(/^\Q$usec\E$/,@possibles)) {
+ &scantron_add_delay(\@delayqueue,$line,
+ "No role with manage grades privilege in student's section ($usec)",3);
+ next;
+ }
+ }
my $user = $uname.':'.$usec;
($uname,$udom)=split(/:/,$uname);
@@ -8601,9 +9298,14 @@ SCANTRONFORM
}
if ((exists($grader_randomlists_by_symb{$ressymb})) ||
(ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
+ my $currcode;
+ if (exists($grader_randomlists_by_symb{$ressymb})) {
+ $currcode = $scancode;
+ }
my ($analysis,$parts) =
&scantron_partids_tograde($resource,$env{'request.course.id'},
- $uname,$udom,undef,$bubbles_per_row);
+ $uname,$udom,undef,$bubbles_per_row,
+ $currcode);
$partids_by_symb{$ressymb} = $parts;
} else {
$partids_by_symb{$ressymb} = $grader_partids_by_symb{$ressymb};
@@ -8636,11 +9338,16 @@ SCANTRONFORM
}
if (($scancode) && ($randomorder || $randompick)) {
- my $parmresult =
- &Apache::lonparmset::storeparm_by_symb($symb,
- '0_examcode',2,$scancode,
- 'string_examcode',$uname,
- $udom);
+ foreach my $key (keys(%symb_for_examcode)) {
+ my $symb_in_map = $symb_for_examcode{$key};
+ if ($symb_in_map ne '') {
+ my $parmresult =
+ &Apache::lonparmset::storeparm_by_symb($symb_in_map,
+ '0_examcode',2,$scancode,
+ 'string_examcode',$uname,
+ $udom);
+ }
+ }
}
$completedstudents{$uname}={'line'=>$line};
if ($env{'form.verifyrecord'}) {
@@ -8859,8 +9566,9 @@ sub grade_student_bubbles {
}
sub scantron_upload_scantron_data {
- my ($r,$symb)=@_;
+ my ($r,$symb) = @_;
my $dom = $env{'request.role.domain'};
+ my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($dom);
my $domdesc = &Apache::lonnet::domain($dom,'description');
$r->print(&Apache::loncommon::coursebrowser_javascript($dom));
my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',
@@ -8900,6 +9608,7 @@ sub scantron_upload_scantron_data {
return;
}
+ '.$formatjs.'
'));
$r->print('
'.&mt('Send bubblesheet data to a course').'
@@ -8915,7 +9624,12 @@ sub scantron_upload_scantron_data {
&Apache::lonhtmlcommon::row_closure().
&Apache::lonhtmlcommon::row_title(&mt('Domain')).
''.$domdesc.
- &Apache::lonhtmlcommon::row_closure().
+ &Apache::lonhtmlcommon::row_closure());
+ if ($formatoptions) {
+ $r->print(&Apache::lonhtmlcommon::row_title($formattitle).$formatoptions.
+ &Apache::lonhtmlcommon::row_closure());
+ }
+ $r->print(
&Apache::lonhtmlcommon::row_title(&mt('File to upload')).
''.
&Apache::lonhtmlcommon::row_closure(1).
@@ -8928,9 +9642,87 @@ sub scantron_upload_scantron_data {
return '';
}
+sub scantron_upload_dataformat {
+ my ($dom) = @_;
+ my ($formatoptions,$formattitle,$formatjs);
+ $formatjs = <<'END';
+function toggleScantab(form) {
+ return;
+}
+END
+ my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$dom);
+ if (ref($domconfig{'scantron'}) eq 'HASH') {
+ if (ref($domconfig{'scantron'}{'config'}) eq 'HASH') {
+ if (keys(%{$domconfig{'scantron'}{'config'}}) > 1) {
+ if (($domconfig{'scantron'}{'config'}{'dat'}) &&
+ (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH')) {
+ if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
+ if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {
+ my ($onclick,$formatextra,$singleline);
+ my @lines = &Apache::lonnet::get_scantronformat_file();
+ my $count = 0;
+ foreach my $line (@lines) {
+ next if (($line =~ /^\#/) || ($line eq ''));
+ $singleline = $line;
+ $count ++;
+ }
+ if ($count > 1) {
+ $formatextra = ''.
+ ''.
+ &mt('Bubblesheet type').': '.
+ &scantron_scantab().' ';
+ $onclick = ' onclick="toggleScantab(this.form);"';
+ $formatjs = <<"END";
+function toggleScantab(form) {
+ var divid = 'bubbletype';
+ if (document.getElementById(divid)) {
+ var radioname = 'fileformat';
+ var num = form.elements[radioname].length;
+ if (num) {
+ for (var i=0; i';
+ }
+ $formattitle = &mt('File format');
+ $formatoptions = ''.(' 'x2).
+ ''.$formatextra;
+ }
+ }
+ }
+ } elsif (keys(%{$domconfig{'scantron'}{'config'}}) == 1) {
+ if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
+ if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {
+ $formattitle = &mt('Bubblesheet type');
+ $formatoptions = &scantron_scantab();
+ }
+ }
+ }
+ }
+ }
+ return ($formatoptions,$formattitle,$formatjs);
+}
sub scantron_upload_scantron_data_save {
- my($r,$symb)=@_;
+ my ($r,$symb) = @_;
my $doanotherupload=
' '."\n";
if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&
!&Apache::lonnet::allowed('usc',
- $env{'form.domainid'}.'_'.$env{'form.courseid'})) {
+ $env{'form.domainid'}.'_'.$env{'form.courseid'}) &&
+ !&Apache::lonnet::allowed('usc',
+ $env{'form.domainid'}.'_'.$env{'form.courseid'}.'/'.$env{'form.coursesec'})) {
$r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")." ");
unless ($symb) {
$r->print($doanotherupload);
@@ -8954,8 +9748,38 @@ sub scantron_upload_scantron_data_save {
&mt('The file: [_1] you attempted to upload contained no information. Please check that you entered the correct filename.',
''.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').''),1));
} else {
- my $result =
- &Apache::lonnet::userfileupload('upfile','','scantron','','','',
+ my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$env{'form.domainid'});
+ my $parser;
+ if (ref($domconfig{'scantron'}) eq 'HASH') {
+ if (ref($domconfig{'scantron'}{'config'}) eq 'HASH') {
+ my $is_csv;
+ my @possibles = keys(%{$domconfig{'scantron'}{'config'}});
+ if (@possibles > 1) {
+ if ($env{'form.fileformat'} eq 'csv') {
+ if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') {
+ if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
+ if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}}) > 1) {
+ $is_csv = 1;
+ }
+ }
+ }
+ }
+ } elsif (@possibles == 1) {
+ if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') {
+ if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
+ if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}}) > 1) {
+ $is_csv = 1;
+ }
+ }
+ }
+ }
+ if ($is_csv) {
+ $parser = $domconfig{'scantron'}{'config'}{'csv'};
+ }
+ }
+ }
+ my $result =
+ &Apache::lonnet::userfileupload('upfile','scantron','scantron',$parser,'','',
$env{'form.courseid'},$env{'form.domainid'});
if ($result =~ m{^/uploaded/}) {
$r->print(
@@ -8964,8 +9788,17 @@ sub scantron_upload_scantron_data_save {
(length($env{'form.upfile'})-1),
''.$result.''));
($uploadedfile) = ($result =~ m{/([^/]+)$});
+ if ($uploadedfile =~ /^scantron_orig_/) {
+ my $logname = $uploadedfile;
+ $logname =~ s/^scantron_orig_//;
+ if ($logname ne '') {
+ my $now = time;
+ my %info = ($logname => { $now => $env{'user.name'}.':'.$env{'user.domain'} });
+ &Apache::lonnet::put('scantronupload',\%info,$env{'form.domainid'},$env{'form.courseid'});
+ }
+ }
$r->print(&validate_uploaded_scantron_file($env{'form.domainid'},
- $env{'form.courseid'},$uploadedfile));
+ $env{'form.courseid'},$symb,$uploadedfile));
} else {
$r->print(
&Apache::lonhtmlcommon::confirm_success(&mt('Upload failed'),1).' '.
@@ -8983,13 +9816,34 @@ sub scantron_upload_scantron_data_save {
}
sub validate_uploaded_scantron_file {
- my ($cdom,$cname,$fname) = @_;
+ my ($cdom,$cname,$symb,$fname,$context,$countsref) = @_;
+
my $scanlines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.$fname);
my @lines;
if ($scanlines ne '-1') {
@lines=split("\n",$scanlines,-1);
}
- my $output;
+ my ($output,$secidx,$checksec,$priv,%crsroleshash,@possibles);
+ $secidx = &Apache::loncoursedata::CL_SECTION();
+ if ($context eq 'download') {
+ $priv = 'mgr';
+ } else {
+ $priv = 'usc';
+ }
+ unless ((&Apache::lonnet::allowed($priv,$env{'request.role.domain'})) ||
+ (($env{'request.course.id'}) &&
+ (&Apache::lonnet::allowed($priv,$env{'request.course.id'})))) {
+ if ($env{'request.course.sec'} ne '') {
+ unless (&Apache::lonnet::allowed($priv,
+ "$env{'request.course.id'}/$env{'request.course.sec'}")) {
+ unless ($context eq 'download') {
+ $output = ''.&mt('You do not have permission to upload bubblesheet data').' ';
+ }
+ return $output;
+ }
+ ($checksec,@possibles)=&gradable_sections();
+ }
+ }
if (@lines) {
my (%counts,$max_match_format);
my ($found_match_count,$max_match_count,$max_match_pct) = (0,0,0);
@@ -9000,9 +9854,9 @@ sub validate_uploaded_scantron_file {
$idmap{$lckey} = $idmap{$key};
}
my %unique_formats;
- my @formatlines = &get_scantronformat_file();
+ my @formatlines = &Apache::lonnet::get_scantronformat_file();
foreach my $line (@formatlines) {
- chomp($line);
+ next if (($line =~ /^\#/) || ($line eq ''));
my @config = split(/:/,$line);
my $idstart = $config[5];
my $idlength = $config[6];
@@ -9019,6 +9873,8 @@ sub validate_uploaded_scantron_file {
%{$counts{$key}} = (
'found' => 0,
'total' => 0,
+ 'totalanysec' => 0,
+ 'othersec' => 0,
);
foreach my $line (@lines) {
next if ($line =~ /^#/);
@@ -9026,6 +9882,23 @@ sub validate_uploaded_scantron_file {
my $id = substr($line,$idstart-1,$idlength);
$id = lc($id);
if (exists($idmap{$id})) {
+ if ($checksec ne '') {
+ $counts{$key}{'totalanysec'} ++;
+ if (ref($classlist->{$idmap{$id}}) eq 'ARRAY') {
+ my $stusec = $classlist->{$idmap{$id}}->[$secidx];
+ if ($stusec ne $checksec) {
+ if (@possibles) {
+ unless (grep(/^\Q$stusec\E$/,@possibles)) {
+ $counts{$key}{'othersec'} ++;
+ next;
+ }
+ } else {
+ $counts{$key}{'othersec'} ++;
+ next;
+ }
+ }
+ }
+ }
$counts{$key}{'found'} ++;
}
$counts{$key}{'total'} ++;
@@ -9040,7 +9913,7 @@ sub validate_uploaded_scantron_file {
}
}
}
- if (ref($unique_formats{$max_match_format}) eq 'ARRAY') {
+ if ((ref($unique_formats{$max_match_format}) eq 'ARRAY') && ($context ne 'download')) {
my $format_descs;
my $numwithformat = @{$unique_formats{$max_match_format}};
for (my $i=0; $i<$numwithformat; $i++) {
@@ -9085,13 +9958,179 @@ sub validate_uploaded_scantron_file {
''.&mt('The course roster is not up to date.').''.
'';
}
+ if (($checksec ne '') && (ref($counts{$max_match_format}) eq 'HASH')) {
+ if ($counts{$max_match_format}{'othersec'}) {
+ my $percent_nongrade = (100*$counts{$max_match_format}{'othersec'})/($counts{$max_match_format}{'totalanysec'});
+ my $showpct = sprintf("%.0f",$percent_nongrade).'%';
+ my $confirmdel = &mt('Are you sure you want to permanently delete this file?');
+ &js_escape(\$confirmdel);
+ $output .= ''.
+ &mt('Comparison of student IDs in the uploaded file with the course roster found [_1][quant,_2,match,matches][_3] for students in section(s) for which none of your role(s) have privileges to modify grades',
+ '',$counts{$max_match_format}{'othersec'},'').
+ ' '.
+ &mt('Unless you are assigned role(s) which allow modification of grades in additional sections, [_1] of the records in this file will be automatically excluded when you perform bubblesheet grading.',''.$showpct.'').
+ ' '.
+ &mt('If you prefer to delete the file now, use: [_1]').
+ ' ';
+ }
+ }
}
- } else {
+ if (($context eq 'download') && ($checksec ne '')) {
+ if ((ref($countsref) eq 'HASH') && (ref($counts{$max_match_format}) eq 'HASH')) {
+ $countsref->{'totalanysec'} = $counts{$max_match_format}{'totalanysec'};
+ $countsref->{'othersec'} = $counts{$max_match_format}{'othersec'};
+ }
+ }
+ } elsif ($context ne 'download') {
$output = ''.&mt('Uploaded file contained no data').' ';
}
return $output;
}
+sub gradable_sections {
+ my $checksec = $env{'request.course.sec'};
+ my @oksecs;
+ if ($checksec) {
+ my %availablesecs = §ions_grade_privs();
+ if (ref($availablesecs{'mgr'}) eq 'ARRAY') {
+ foreach my $sec (@{$availablesecs{'mgr'}}) {
+ unless (grep(/^\Q$sec\E$/,@oksecs)) {
+ push(@oksecs,$sec);
+ }
+ }
+ if (grep(/^all$/,@oksecs)) {
+ undef($checksec);
+ }
+ }
+ }
+ return($checksec,@oksecs);
+}
+
+sub sections_grade_privs {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my %availablesecs = (
+ mgr => [],
+ vgr => [],
+ usc => [],
+ );
+ my $ccrole = 'cc';
+ if ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Community') {
+ $ccrole = 'co';
+ }
+ my %crsroleshash = &Apache::lonnet::get_my_roles($env{'user.name'},$env{'user.domain'},
+ 'userroles',['active'],
+ [$ccrole,'in','cr'],$cdom,1);
+ my $crsid = $cnum.':'.$cdom;
+ foreach my $item (keys(%crsroleshash)) {
+ next unless ($item =~ /^$crsid\:/);
+ my ($crsnum,$crsdom,$role,$sec) = split(/\:/,$item);
+ my $suffix = "/$cdom/$cnum./$cdom/$cnum";
+ if ($sec ne '') {
+ $suffix = "/$cdom/$cnum/$sec./$cdom/$cnum/$sec";
+ }
+ if (($role eq $ccrole) || ($role eq 'in')) {
+ foreach my $priv ('mgr','vgr','usc') {
+ unless (grep(/^all$/,@{$availablesecs{$priv}})) {
+ if ($sec eq '') {
+ $availablesecs{$priv} = ['all'];
+ } elsif ($sec ne $env{'request.course.sec'}) {
+ unless (grep(/^\Q$sec\E$/,@{$availablesecs{$priv}})) {
+ push(@{$availablesecs{$priv}},$sec);
+ }
+ }
+ }
+ }
+ } elsif ($role =~ m{^cr/}) {
+ foreach my $priv ('mgr','vgr','usc') {
+ unless (grep(/^all$/,@{$availablesecs{$priv}})) {
+ if ($env{"user.priv.$role.$suffix"} =~ /:$priv&/) {
+ if ($sec eq '') {
+ $availablesecs{$priv} = ['all'];
+ } elsif ($sec ne $env{'request.course.sec'}) {
+ unless (grep(/^\Q$sec\E$/,@{$availablesecs{$priv}})) {
+ push(@{$availablesecs{$priv}},$sec);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return %availablesecs;
+}
+
+sub scantron_upload_delete {
+ my ($r,$symb) = @_;
+ my $filename = $env{'form.uploadedfile'};
+ if ($filename =~ /^scantron_orig_/) {
+ if (&Apache::lonnet::allowed('usc',$env{'form.domainid'}) ||
+ &Apache::lonnet::allowed('usc',
+ $env{'form.domainid'}.'_'.$env{'form.courseid'}) ||
+ &Apache::lonnet::allowed('usc',
+ $env{'form.domainid'}.'_'.$env{'form.courseid'}.'/'.$env{'form.coursesec'})) {
+ my $uploadurl = '/uploaded/'.$env{'form.domainid'}.'/'.$env{'form.courseid'}.'/'.$env{'form.uploadedfile'};
+ my $retrieval = &Apache::lonnet::getfile($uploadurl);
+ if ($retrieval eq '-1') {
+ $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).' '.
+ &mt('File requested for deletion not found.'));
+ } else {
+ $filename =~ s/^scantron_orig_//;
+ if ($filename ne '') {
+ my ($is_valid,$numleft);
+ my %info = &Apache::lonnet::get('scantronupload',[$filename],$env{'form.domainid'},$env{'form.courseid'});
+ if (keys(%info)) {
+ if (ref($info{$filename}) eq 'HASH') {
+ foreach my $timestamp (sort(keys(%{$info{$filename}}))) {
+ if ($info{$filename}{$timestamp} eq $env{'user.name'}.':'.$env{'user.domain'}) {
+ $is_valid = 1;
+ delete($info{$filename}{$timestamp});
+ }
+ }
+ $numleft = scalar(keys(%{$info{$filename}}));
+ }
+ }
+ if ($is_valid) {
+ my $result = &Apache::lonnet::removeuploadedurl($uploadurl);
+ if ($result eq 'ok') {
+ $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion successful')).' ');
+ if ($numleft) {
+ &Apache::lonnet::put('scantronupload',\%info,$env{'form.domainid'},$env{'form.courseid'});
+ } else {
+ &Apache::lonnet::del('scantronupload',[$filename],$env{'form.domainid'},$env{'form.courseid'});
+ }
+ } else {
+ $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).' '.
+ &mt('Result was [_1]',$result));
+ }
+ } else {
+ $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).' '.
+ &mt('File requested for deletion was uploaded by a different user.'));
+ }
+ } else {
+ $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).' '.
+ &mt('Filename of bubblesheet data file requested for deletion is invalid.'));
+ }
+ }
+ } else {
+ $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).' '.
+ &mt('You are not permitted to delete bubblesheet data files from the requested course.'));
+ }
+ } else {
+ $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).' '.
+ &mt('Filename of bubblesheet data file requested for deletion is invalid.'));
+ }
+ return;
+}
+
sub valid_file {
my ($requested_file)=@_;
foreach my $filename (sort(&scantron_filenames())) {
@@ -9101,7 +10140,7 @@ sub valid_file {
}
sub scantron_download_scantron_data {
- my ($r,$symb)=@_;
+ my ($r,$symb) = @_;
my $default_form_data=&defaultFormData($symb);
my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
@@ -9114,6 +10153,29 @@ sub scantron_download_scantron_data {
');
return;
}
+ my (%uploader,$is_owner,%counts,$percent);
+ my %uploader = &Apache::lonnet::get('scantronupload',[$file],$cdom,$cname);
+ if (ref($uploader{$file}) eq 'HASH') {
+ foreach my $timestamp (sort { $a <=> $b } keys(%{$uploader{$file}})) {
+ if ($uploader{$file}{$timestamp} eq $env{'user.name'}.':'.$env{'user.domain'}) {
+ $is_owner = 1;
+ last;
+ }
+ }
+ }
+ unless ($is_owner) {
+ &validate_uploaded_scantron_file($cdom,$cname,$symb,'scantron_orig_'.$file,'download',\%counts);
+ if ($counts{'totalanysec'}) {
+ my $percent_othersec = (100*$counts{'othersec'})/($counts{'totalanysec'});
+ if ($percent_othersec >= 10) {
+ my $showpct = sprintf("%.0f",$percent_othersec).'%';
+ $r->print(''.
+ &mt('The original uploaded file includes [_1] or more of records for students for which none of your roles have rights to modify grades, so files are unavailable for download.',$showpct).
+ ' ');
+ return;
+ }
+ }
+ }
my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;
my $corrected='/uploaded/'.$cdom.'/'.$cname.'/scantron_corrected_'.$file;
my $skipped='/uploaded/'.$cdom.'/'.$cname.'/scantron_skipped_'.$file;
@@ -9141,16 +10203,16 @@ sub checkscantron_results {
my ($r,$symb) = @_;
if (!$symb) {return '';}
my $cid = $env{'request.course.id'};
- my %lettdig = &letter_to_digits();
+ my %lettdig = &Apache::lonnet::letter_to_digits();
my $numletts = scalar(keys(%lettdig));
my $cnum = $env{'course.'.$cid.'.num'};
my $cdom = $env{'course.'.$cid.'.domain'};
my (undef, undef, $sequence) = &Apache::lonnet::decode_symb($env{'form.selectpage'});
my %record;
my %scantron_config =
- &Apache::grades::get_scantron_config($env{'form.scantron_format'});
+ &Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
- my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile();
+ my ($scanlines,$scan_data)=&scantron_getfile();
my $classlist=&Apache::loncoursedata::get_classlist();
my %idmap=&Apache::grades::username_to_idmap($classlist);
my $navmap=Apache::lonnavmaps::navmap->new();
@@ -9164,6 +10226,17 @@ sub checkscantron_results {
if (ref($map)) {
$randomorder=$map->randomorder();
$randompick=$map->randompick();
+ unless ($randomorder || $randompick) {
+ foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
+ if ($res->randomorder()) {
+ $randomorder = 1;
+ }
+ if ($res->randompick()) {
+ $randompick = 1;
+ }
+ last if ($randomorder || $randompick);
+ }
+ }
}
my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
my $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
@@ -9258,10 +10331,14 @@ sub checkscantron_results {
my $ressymb = $resource->symb();
if ((exists($grader_randomlists_by_symb{$ressymb})) ||
(ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
+ my $currcode;
+ if (exists($grader_randomlists_by_symb{$ressymb})) {
+ $currcode = $scancode;
+ }
(my $analysis,$parts) =
&scantron_partids_tograde($resource,$env{'request.course.id'},
$username,$domain,undef,
- $bubbles_per_row);
+ $bubbles_per_row,$currcode);
} else {
$parts = $grader_partids_by_symb{$ressymb};
}
@@ -9468,23 +10545,6 @@ sub verify_scantron_grading {
return ($counter,$record);
}
-sub letter_to_digits {
- my %lettdig = (
- A => 1,
- B => 2,
- C => 3,
- D => 4,
- E => 5,
- F => 6,
- G => 7,
- H => 8,
- I => 9,
- J => 0,
- );
- return %lettdig;
-}
-
-
#-------- end of section for handling grading scantron forms -------
#
#-------------------------------------------------------------------
@@ -9530,37 +10590,47 @@ sub grading_menu {
$fields{'command'} = 'initialverifyreceipt';
my $url5 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
-
+
+ my %permissions;
+ if ($perm{'mgr'}) {
+ $permissions{'either'} = 'F';
+ $permissions{'mgr'} = 'F';
+ }
+ if ($perm{'vgr'}) {
+ $permissions{'either'} = 'F';
+ $permissions{'vgr'} = 'F';
+ }
+
my @menu = ({ categorytitle=>'Hand Grading',
items =>[
{ linktext => 'Select individual students to grade',
url => $url1a,
- permission => 'F',
+ permission => $permissions{'either'},
icon => 'grade_students.png',
linktitle => 'Grade current resource for a selection of students.'
},
- { linktext => 'Grade ungraded submissions.',
+ { linktext => 'Grade ungraded submissions',
url => $url1b,
- permission => 'F',
+ permission => $permissions{'either'},
icon => 'ungrade_sub.png',
linktitle => 'Grade all submissions that have not been graded yet.'
},
{ linktext => 'Grading table',
url => $url1c,
- permission => 'F',
+ permission => $permissions{'either'},
icon => 'grading_table.png',
linktitle => 'Grade current resource for all students.'
},
{ linktext => 'Grade page/folder for one student',
url => $url1d,
- permission => 'F',
+ permission => $permissions{'either'},
icon => 'grade_PageFolder.png',
linktitle => 'Grade all resources in current page/sequence/folder for one student.'
},
{ linktext => 'Download submissions',
url => $url1e,
- permission => 'F',
+ permission => $permissions{'either'},
icon => 'download_sub.png',
linktitle => 'Download all students submissions.'
}]},
@@ -9569,25 +10639,25 @@ sub grading_menu {
{ linktext => 'Upload Scores',
url => $url2,
- permission => 'F',
+ permission => $permissions{'mgr'},
icon => 'uploadscores.png',
linktitle => 'Specify a file containing the class scores for current resource.'
},
{ linktext => 'Process Clicker',
url => $url3,
- permission => 'F',
+ permission => $permissions{'mgr'},
icon => 'addClickerInfoFile.png',
linktitle => 'Specify a file containing the clicker information for this resource.'
},
{ linktext => 'Grade/Manage/Review Bubblesheets',
url => $url4,
- permission => 'F',
+ permission => $permissions{'mgr'},
icon => 'bubblesheet.png',
linktitle => 'Grade bubblesheet exams, upload/download bubblesheet data files, and review previously graded bubblesheet exams.'
},
{ linktext => 'Verify Receipt Number',
url => $url5,
- permission => 'F',
+ permission => $permissions{'either'},
icon => 'receipt_number.png',
linktitle => 'Verify a system-generated receipt number for correct problem solution.'
}
@@ -9605,7 +10675,6 @@ sub grading_menu {
return $Str;
}
-
sub ungraded {
my ($request)=@_;
&submit_options($request);
@@ -9633,12 +10702,13 @@ sub submit_options_table {
my ($request,$symb) = @_;
if (!$symb) {return '';}
&commonJSfunctions($request);
+ my $is_tool = ($symb =~ /ext\.tool$/);
my $result;
$result.='';
return $result;
}
sub selectfield {
- my ($full)=@_;
- my %options =
- (&substatus_options,
- 'select_form_order' => ['yes','queued','graded','incorrect','all']);
+ my ($full,$is_tool)=@_;
+ my %options;
+ if ($is_tool) {
+ %options =
+ (&transtatus_options,
+ 'select_form_order' => ['yes','incorrect','all']);
+ } else {
+ %options =
+ (&substatus_options,
+ 'select_form_order' => ['yes','queued','graded','incorrect','all']);
+ }
+
+ #
+ # PrepareClasslist() needs to be called to avoid getting a sections list
+ # for a different course from the @Sections global in lonstatistics.pm,
+ # populated by an earlier request.
+ #
+ &Apache::lonstatistics::PrepareClasslist();
+
my $result='
';
if ($full) {
- $result.='
+ my $heading = &mt('Submission Status');
+ if ($is_tool) {
+ $heading = &mt('Transaction Status');
+ }
+ $result.='
';
@@ -9741,13 +10849,21 @@ sub substatus_options {
);
}
+sub transtatus_options {
+ return &Apache::lonlocal::texthash(
+ 'yes' => 'with score transactions',
+ 'incorrect' => 'with less than full credit',
+ 'all' => 'with any status',
+ );
+}
+
sub reset_perm {
undef(%perm);
}
sub init_perm {
&reset_perm();
- foreach my $test_perm ('vgr','mgr','opa') {
+ foreach my $test_perm ('vgr','mgr','opa','usc') {
my $scope = $env{'request.course.id'};
if (!($perm{$test_perm}=&Apache::lonnet::allowed($test_perm,$scope))) {
@@ -9935,12 +11051,12 @@ ENDUPFORM
ENDGRADINGFORM
- $result.=''.&Apache::loncommon::end_data_table_row().
+ $result.=''.&Apache::loncommon::end_data_table_row().
&Apache::loncommon::start_data_table_row().' '.(<$pcorrect:
-'
+
ENDPERCFORM
$result.=' | '.
&Apache::loncommon::end_data_table_row().
@@ -9949,7 +11065,7 @@ ENDPERCFORM
}
sub process_clicker_file {
- my ($r,$symb)=@_;
+ my ($r,$symb) = @_;
if (!$symb) {return '';}
my %Saveable_Parameters=&clicker_grading_parameters();
@@ -10021,6 +11137,22 @@ sub process_clicker_file {
' '.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').''),1);
return $result;
}
+ my $mimetype;
+ if ($env{'form.upfiletype'} eq 'iclicker') {
+ my $mm = new File::MMagic;
+ $mimetype = $mm->checktype_contents($env{'form.upfile'});
+ unless (($mimetype eq 'text/plain') || ($mimetype eq 'text/html')) {
+ $result.= ' '.
+ &Apache::lonhtmlcommon::confirm_success(
+ &mt('File format is neither csv (iclicker 6) nor xml (iclicker 7)'),1).' ';
+ return $result;
+ }
+ } elsif (($env{'form.upfiletype'} ne 'interwrite') && ($env{'form.upfiletype'} ne 'turning')) {
+ $result .= ' '.
+ &Apache::lonhtmlcommon::confirm_success(
+ &mt('Invalid clicker type: choose one of: i>clicker, Interwrite PRS, or Turning Technologies.'),1).' ';
+ return $result;
+ }
# Were able to get all the info needed, now analyze the file
@@ -10047,12 +11179,14 @@ ENDHEADER
my $errormsg='';
my $number=0;
if ($env{'form.upfiletype'} eq 'iclicker') {
- ($errormsg,$number)=&iclicker_eval(\@questiontitles,\%responses);
- }
- if ($env{'form.upfiletype'} eq 'interwrite') {
+ if ($mimetype eq 'text/plain') {
+ ($errormsg,$number)=&iclicker_eval(\@questiontitles,\%responses);
+ } elsif ($mimetype eq 'text/html') {
+ ($errormsg,$number)=&iclickerxml_eval(\@questiontitles,\%responses);
+ }
+ } elsif ($env{'form.upfiletype'} eq 'interwrite') {
($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);
- }
- if ($env{'form.upfiletype'} eq 'turning') {
+ } elsif ($env{'form.upfiletype'} eq 'turning') {
($errormsg,$number)=&turning_eval(\@questiontitles,\%responses);
}
$result.=' '.&mt('Found [_1] question(s)',$number).' '.
@@ -10105,7 +11239,7 @@ ENDHEADER
"\n".&mt("Username").": ".
"\n".&mt("Domain").": ".
&Apache::loncommon::select_dom_form($env{'course.'.$env{'request.course.id'}.'.domain'},'udom'.$id).' '.
- &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id,0,$id);
+ &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id,'',$id);
$unknown_count++;
}
}
@@ -10160,6 +11294,49 @@ sub iclicker_eval {
return ($errormsg,$number);
}
+sub iclickerxml_eval {
+ my ($questiontitles,$responses)=@_;
+ my $number=0;
+ my $errormsg='';
+ my @state;
+ my %respbyid;
+ my $p = HTML::Parser->new
+ (
+ xml_mode => 1,
+ start_h =>
+ [sub {
+ my ($tagname,$attr) = @_;
+ push(@state,$tagname);
+ if ("@state" eq "ssn p") {
+ my $title = $attr->{qn};
+ $title =~ s/(^\s+|\s+$)//g;
+ $questiontitles->[$number]=$title;
+ } elsif ("@state" eq "ssn p v") {
+ my $id = $attr->{id};
+ my $entry = $attr->{ans};
+ $id=~s/^[\#0]+//;
+ $entry =~s/[^a-zA-Z0-9\.\*\-\+]+//g;
+ $respbyid{$id}[$number] = $entry;
+ }
+ }, "tagname, attr"],
+ end_h =>
+ [sub {
+ my ($tagname) = @_;
+ if ("@state" eq "ssn p") {
+ $number++;
+ }
+ pop(@state);
+ }, "tagname"],
+ );
+
+ $p->parse($env{'form.upfile'});
+ $p->eof;
+ foreach my $id (keys(%respbyid)) {
+ $responses->{$id}=join(',',@{$respbyid{$id}});
+ }
+ return ($errormsg,$number);
+}
+
sub interwrite_eval {
my ($questiontitles,$responses)=@_;
my $number=0;
@@ -10218,7 +11395,7 @@ sub turning_eval {
sub assign_clicker_grades {
- my ($r,$symb)=@_;
+ my ($r,$symb) = @_;
if (!$symb) {return '';}
# See which part we are saving to
my $res_error;
@@ -10229,11 +11406,11 @@ sub assign_clicker_grades {
# FIXME: This should probably look for the first handgradeable part
my $part=$$partlist[0];
# Start screen output
- my $result=&Apache::loncommon::start_data_table().
- &Apache::loncommon::start_data_table_header_row().
- ' '.&mt('Assigning grades based on clicker file').' | '.
- &Apache::loncommon::end_data_table_header_row().
- &Apache::loncommon::start_data_table_row().' ';
+ my $result = &Apache::loncommon::start_data_table().
+ &Apache::loncommon::start_data_table_header_row().
+ ' | '.&mt('Assigning grades based on clicker file').' | '.
+ &Apache::loncommon::end_data_table_header_row().
+ &Apache::loncommon::start_data_table_row().' ';
# Get correct result
# FIXME: Possibly need delimiter other than ":"
my @correct=();
@@ -10295,7 +11472,7 @@ sub assign_clicker_grades {
for (my $i=0;$i<$number;$i++) {
if ($correct[$i] eq '-') {
$realnumber--;
- } elsif (($answer[$i]) || ($answer[$i]=~/^[0\.]+$/)) {
+ } elsif (($answer[$i]) || ($answer[$i]=~/^[0\.]+$/)) {
if ($gradingmechanism eq 'attendance') {
$sum+=$pcorrect;
} elsif ($correct[$i] eq '*') {
@@ -10354,28 +11531,111 @@ sub navmap_errormsg {
}
sub startpage {
- my ($r,$symb,$crumbs,$onlyfolderflag,$nodisplayflag,$stuvcurrent,$stuvdisp,$nomenu,$js) = @_;
+ my ($r,$symb,$crumbs,$onlyfolderflag,$nodisplayflag,$stuvcurrent,$stuvdisp,$nomenu,$head_extra,$onload,$divforres) = @_;
+ my %args;
+ if ($onload) {
+ my %loaditems = (
+ 'onload' => $onload,
+ );
+ $args{'add_entries'} = \%loaditems;
+ }
if ($nomenu) {
- $r->print(&Apache::loncommon::start_page("Student's Version",$js,{'only_body' => '1'}));
+ $args{'only_body'} = 1;
+ $r->print(&Apache::loncommon::start_page("Student's Version",$head_extra,\%args));
} else {
- unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});
- $r->print(&Apache::loncommon::start_page('Grading',$js,
- {'bread_crumbs' => $crumbs}));
- &Apache::lonquickgrades::startGradeScreen($r,($env{'form.symb'}?'probgrading':'grading'));
+ if ($env{'request.course.id'}) {
+ unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});
+ }
+ $args{'bread_crumbs'} = $crumbs;
+ $r->print(&Apache::loncommon::start_page('Grading',$head_extra,\%args));
+ if ($env{'request.course.id'}) {
+ &Apache::lonquickgrades::startGradeScreen($r,($env{'form.symb'}?'probgrading':'grading'));
+ }
}
unless ($nodisplayflag) {
- $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp));
+ $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp,$divforres));
}
}
sub select_problem {
my ($r)=@_;
$r->print(''.&mt('Select the problem or one of the problems you want to grade').'');
}
+#----- display problem, answer, and submissions for a single student (no grading)
+
+sub view_as_user {
+ my ($symb,$vuname,$vudom,$hasperm) = @_;
+ my $plainname = &Apache::loncommon::plainname($vuname,$vudom,'lastname');
+ my $displayname = &nameUserString('',$plainname,$vuname,$vudom);
+ my $output = &Apache::loncommon::get_student_view($symb,$vuname,$vudom,
+ $env{'request.course.id'},
+ undef,{'disable_submit' => 1}).
+ "\n\n".
+ ''.
+ ' '.$displayname.''.
+ "\n".
+ &Apache::loncommon::track_student_link('View recent activity',
+ $vuname,$vudom,'check').' '.
+ "\n";
+ if (&Apache::lonnet::allowed('opa',$env{'request.course.id'}) ||
+ (($env{'request.course.sec'} ne '') &&
+ &Apache::lonnet::allowed('opa',$env{'request.course.id'}.'/'.$env{'request.course.sec'}))) {
+ $output .= &Apache::loncommon::pprmlink(&mt('Set/Change parameters'),
+ $vuname,$vudom,$symb,'check');
+ }
+ $output .= "\n";
+ my $companswer = &Apache::loncommon::get_student_answers($symb,$vuname,$vudom,
+ $env{'request.course.id'});
+ $companswer=~s| ||g;
+ $companswer=~s|name="submit"|name="would_have_been_submit"|g;
+ $output .= ' '.
+ ' '.&mt('Correct answer for[_1]',$displayname).''.
+ $companswer.
+ ''."\n";
+ my $is_tool = ($symb =~ /ext\.tool$/);
+ my ($essayurl,%coursedesc_by_cid);
+ (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);
+ my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$vudom,$vuname);
+ my $res_error;
+ my ($partlist,$handgrade,$responseType,$numresp,$numessay) =
+ &response_type($symb,\$res_error);
+ my $fullname;
+ my $collabinfo;
+ if ($numessay) {
+ unless ($hasperm) {
+ &init_perm();
+ }
+ ($collabinfo,$fullname)=
+ &check_collaborators($symb,$vuname,$vudom,\%record,$handgrade,0);
+ unless ($hasperm) {
+ &reset_perm();
+ }
+ }
+ my $checkIcon = ' ';
+ my ($lastsubonly,$partinfo) =
+ &show_last_submission($vuname,$vudom,$symb,$essayurl,$responseType,'datesub',
+ '',$fullname,\%record,\%coursedesc_by_cid);
+ $output .= ' '.
+ ' '.&mt('Submissions').''."\n".$collabinfo."\n";
+ if (($numresp > $numessay) & !$is_tool) {
+ $output .=' '.
+ &mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon).
+ " \n";
+ }
+ $output .= $partinfo;
+ $output .= $lastsubonly;
+ $output .= &displaySubByDates($symb,\%record,$partlist,$responseType,$checkIcon,$vuname,$vudom);
+ $output .= ' '."\n";
+ return $output;
+}
+
sub handler {
my $request=$_[0];
&reset_caches();
@@ -10428,24 +11688,49 @@ sub handler {
&select_problem($request);
} else {
if ($command eq 'submission' && $perm{'vgr'}) {
- my ($stuvcurrent,$stuvdisp,$versionform,$js);
+ my ($stuvcurrent,$stuvdisp,$versionform,$js,$onload);
if (($env{'form.student'} ne '') && ($env{'form.userdom'} ne '')) {
($stuvcurrent,$stuvdisp,$versionform,$js) =
&choose_task_version_form($symb,$env{'form.student'},
$env{'form.userdom'});
}
- &startpage($request,$symb,[{href=>"", text=>"Student Submissions"}],undef,undef,$stuvcurrent,$stuvdisp,undef,$js);
+ my $divforres;
+ if ($env{'form.student'} eq '') {
+ $js .= &part_selector_js();
+ $onload = "toggleParts('gradesub');";
+ } else {
+ $divforres = 1;
+ }
+ my $head_extra = $js;
+ unless ($env{'form.vProb'} eq 'no') {
+ my $csslinks = &Apache::loncommon::css_links($symb);
+ if ($csslinks) {
+ $head_extra .= "\n$csslinks";
+ }
+ }
+ &startpage($request,$symb,[{href=>"", text=>"Student Submissions"}],undef,undef,
+ $stuvcurrent,$stuvdisp,undef,$head_extra,$onload,$divforres);
if ($versionform) {
+ if ($divforres) {
+ $request->print('');
+ }
$request->print($versionform);
}
- $request->print(' ');
- ($env{'form.student'} eq '' ? &listStudents($request,$symb) : &submission($request,0,0,$symb));
+ ($env{'form.student'} eq '' ? &listStudents($request,$symb,'',$divforres) : &submission($request,0,0,$symb,$divforres,$command));
} elsif ($command eq 'versionsub' && $perm{'vgr'}) {
my ($stuvcurrent,$stuvdisp,$versionform,$js) =
&choose_task_version_form($symb,$env{'form.student'},
$env{'form.userdom'},
$env{'form.inhibitmenu'});
- &startpage($request,$symb,[{href=>"", text=>"Previous Student Version"}],undef,undef,$stuvcurrent,$stuvdisp,$env{'form.inhibitmenu'},$js);
+ my $head_extra = $js;
+ unless ($env{'form.vProb'} eq 'no') {
+ my $csslinks = &Apache::loncommon::css_links($symb);
+ if ($csslinks) {
+ $head_extra .= "\n$csslinks";
+ }
+ }
+ &startpage($request,$symb,[{href=>"", text=>"Previous Student Version"}],undef,undef,
+ $stuvcurrent,$stuvdisp,$env{'form.inhibitmenu'},$head_extra);
if ($versionform) {
$request->print($versionform);
}
@@ -10456,10 +11741,14 @@ sub handler {
{href=>'',text=>'Select student'}],1,1);
&pickStudentPage($request,$symb);
} elsif ($command eq 'displayPage' && $perm{'vgr'}) {
+ my $csslinks;
+ unless ($env{'form.vProb'} eq 'no') {
+ $csslinks = &Apache::loncommon::css_links($symb,'map');
+ }
&startpage($request,$symb,
[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
{href=>'',text=>'Select student'},
- {href=>'',text=>'Grade student'}],1,1);
+ {href=>'',text=>'Grade student'}],1,1,undef,undef,undef,$csslinks);
&displayPage($request,$symb);
} elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {
&startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
@@ -10468,8 +11757,12 @@ sub handler {
{href=>'',text=>'Store grades'}],1,1);
&updateGradeByPage($request,$symb);
} elsif ($command eq 'processGroup' && $perm{'vgr'}) {
+ my $csslinks;
+ unless ($env{'form.vProb'} eq 'no') {
+ $csslinks = &Apache::loncommon::css_links($symb);
+ }
&startpage($request,$symb,[{href=>'',text=>'...'},
- {href=>'',text=>'Modify grades'}]);
+ {href=>'',text=>'Modify grades'}],undef,undef,undef,undef,undef,$csslinks,undef,1);
&processGroup($request,$symb);
} elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {
&startpage($request,$symb);
@@ -10478,7 +11771,10 @@ sub handler {
&startpage($request,$symb,[{href=>'',text=>'Select individual students to grade'}]);
$request->print(&submit_options($request,$symb));
} elsif ($command eq 'ungraded' && $perm{'vgr'}) {
- &startpage($request,$symb,[{href=>'',text=>'Grade ungraded submissions'}]);
+ my $js = &part_selector_js();
+ my $onload = "toggleParts('gradesub');";
+ &startpage($request,$symb,[{href=>'',text=>'Grade ungraded submissions'}],
+ undef,undef,undef,undef,undef,$js,$onload);
$request->print(&listStudents($request,$symb,'graded'));
} elsif ($command eq 'table' && $perm{'vgr'}) {
&startpage($request,$symb,[{href=>"", text=>"Grading table"}]);
@@ -10544,7 +11840,8 @@ sub handler {
&startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
$request->print(&csvuploadassign($request,$symb));
} elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {
- &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
+ &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,
+ undef,undef,undef,undef,'toggleScantab(document.rules);');
$request->print(&scantron_selectphase($request,undef,$symb));
} elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {
&startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
@@ -10556,29 +11853,36 @@ sub handler {
&startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
$request->print(&scantron_process_students($request,$symb));
} elsif ($command eq 'scantronupload' &&
- (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||
- &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {
- &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
+ (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
+ &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,
+ undef,undef,undef,undef,'toggleScantab(document.rules);');
$request->print(&scantron_upload_scantron_data($request,$symb));
} elsif ($command eq 'scantronupload_save' &&
- (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||
- &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {
+ (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
&startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
$request->print(&scantron_upload_scantron_data_save($request,$symb));
- } elsif ($command eq 'scantron_download' &&
- &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
+ } elsif ($command eq 'scantron_download' && ($perm{'usc'} || $perm{'mgr'})) {
&startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
$request->print(&scantron_download_scantron_data($request,$symb));
+ } elsif ($command eq 'scantronupload_delete' &&
+ (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
+ &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
+ &scantron_upload_delete($request,$symb);
} elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {
&startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
$request->print(&checkscantron_results($request,$symb));
} elsif ($command eq 'downloadfilesselect' && $perm{'vgr'}) {
- &startpage($request,$symb,[{href=>'', text=>'Select which submissions to download'}]);
+ my $js = &part_selector_js();
+ my $onload = "toggleParts('gradingMenu');";
+ &startpage($request,$symb,[{href=>'', text=>'Select which submissions to download'}],
+ undef,undef,undef,undef,undef,$js,$onload);
$request->print(&submit_options_download($request,$symb));
} elsif ($command eq 'downloadfileslink' && $perm{'vgr'}) {
&startpage($request,$symb,
[{href=>&href_symb_cmd($symb,'downloadfilesselect'), text=>'Select which submissions to download'},
- {href=>'', text=>'Download submissions'}]);
+ {href=>'', text=>'Download submitted files'}],
+ undef,undef,undef,undef,undef,undef,undef,1);
+ $request->print('');
&submit_download_link($request,$symb);
} elsif ($command) {
&startpage($request,$symb,[{href=>'', text=>'Access denied'}]);
@@ -10590,7 +11894,7 @@ sub handler {
}
if ($env{'form.inhibitmenu'}) {
$request->print(&Apache::loncommon::end_page());
- } else {
+ } elsif ($env{'request.course.id'}) {
&Apache::lonquickgrades::endGradeScreen($request);
}
&reset_caches();
@@ -10741,7 +12045,7 @@ Side Effects: None.
$r - Apache request object
$i - number of the current scanline
$scan_record - hash ref as returned from &scantron_parse_scanline()
- $scan_config - hash ref as returned from &get_scantron_config()
+ $scan_config - hash ref as returned from &Apache::lonnet::get_scantron_config()
$line - full contents of the current scanline
$error - error condition, valid values are
'incorrectCODE', 'duplicateCODE',
@@ -10758,8 +12062,8 @@ Side Effects: None.
- missingbubble - array ref of the bubble lines that have missing
bubble errors
- $randomorder - True if exam folder has randomorder set
- $randompick - True if exam folder has randompick set
+ $randomorder - True if exam folder (or a sub-folder) has randomorder set
+ $randompick - True if exam folder (or a sub-folder) has randompick set
$respnumlookup - Reference to HASH mapping question numbers in bubble lines
for current line to question number used for same question
in "Master Seqence" (as seen by Course Coordinator).
@@ -10828,7 +12132,12 @@ Side Effects: None.
=item scantron_upload_scantron_data_save() :
Adds a provided bubble information data file to the course if user
- has the correct privileges to do so.
+ has the correct privileges to do so.
+
+= item scantron_upload_delete() :
+
+ Deletes a previously uploaded bubble information data file, if user
+ was the one who uploaded the file, and has the privileges to do so.
=item valid_file() :
| |