--- loncom/homework/grades.pm 2021/12/17 15:16:51 1.786
+++ loncom/homework/grades.pm 2023/02/12 21:01:30 1.792
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.786 2021/12/17 15:16:51 raeburn Exp $
+# $Id: grades.pm,v 1.792 2023/02/12 21:01:30 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -2206,7 +2206,7 @@ sub files_exist {
my ($uname,$udom,$fullname) = split(/:/,$student);
my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},
$udom,$uname);
- my ($string,$timestamp)= &get_last_submission(\%record);
+ my ($string)= &get_last_submission(\%record);
foreach my $submission (@$string) {
my ($partid,$respid) =
($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
@@ -2558,20 +2558,33 @@ sub submission {
# (3) All transactions (by date)
# (4) The whole record (with detailed information for all transactions)
- my ($string,$timestamp)= &get_last_submission(\%record,$is_tool);
+ my ($string,$timestamp,$lastgradetime,$lastsubmittime) =
+ &get_last_submission(\%record,$is_tool);
my $lastsubonly;
- if ($$timestamp eq '') {
- $lastsubonly.='
'.$$string[0].'
';
+ if ($timestamp eq '') {
+ $lastsubonly.=''.$string->[0].'
';
} elsif ($is_tool) {
$lastsubonly =
''
- .''.&mt('Date Grade Passed Back:').' '.$$timestamp."
\n";
+ .''.&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:').' '.$$timestamp."\n";
+ .''.&mt('Date Submitted:').' '.$shownsubmdate."\n";
+ if ($showngradedate) {
+ $lastsubonly .= '
'.&mt('Date Graded:').' '.$showngradedate."\n";
+ }
my %seenparts;
my @part_response_id = &flatten_responseType($responseType);
@@ -2903,17 +2916,45 @@ sub check_collaborators {
#--- Retrieve the last submission for all the parts
sub get_last_submission {
my ($returnhash,$is_tool)=@_;
- my (@string,$timestamp,%lasthidden);
+ 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 =
@@ -2977,7 +3018,7 @@ sub get_last_submission {
$string[0] =
''.$msg.'';
}
- return (\@string,\$timestamp);
+ return (\@string,$timestamp,$lastgradetime,$lastsubmittime);
}
#--- High light keywords, with style choosen by user.
@@ -5731,6 +5772,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);
@@ -5740,13 +5782,13 @@ 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($part);
+ my @types = $curRes->responseType($partid);
if (grep(/^essay$/,@types)) {
$queueable{$partid} = 1;
} else {
- my @ids = $curRes->responseIds($part);
+ my @ids = $curRes->responseIds($partid);
for (my $i=0; $i < scalar(@ids); $i++) {
- my $hndgrd = &Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i].
+ my $hndgrd = &Apache::lonnet::EXT('resource.'.$partid.'_'.$ids[$i].
'.handgrade',$symb);
if (lc($hndgrd) eq 'yes') {
$queueable{$partid} = 1;
@@ -6731,9 +6773,12 @@ sub scantron_parse_scanline {
}
sub get_master_seq {
- my ($resources,$master_seq,$symb_to_resource) = @_;
+ my ($resources,$master_seq,$symb_to_resource,$need_symb_in_map,$symb_for_examcode) = @_;
return unless ((ref($resources) eq 'ARRAY') && (ref($master_seq) eq 'ARRAY') &&
(ref($symb_to_resource) eq 'HASH'));
+ if ($need_symb_in_map) {
+ return unless (ref($symb_for_examcode) eq 'HASH');
+ }
my $resource_error;
foreach my $resource (@{$resources}) {
my $ressymb;
@@ -6741,6 +6786,14 @@ sub get_master_seq {
$ressymb = $resource->symb();
push(@{$master_seq},$ressymb);
$symb_to_resource->{$ressymb} = $resource;
+ if ($need_symb_in_map) {
+ unless ($resource->is_map()) {
+ my $map=(&Apache::lonnet::decode_symb($ressymb))[0];
+ unless (exists($symb_for_examcode->{$map})) {
+ $symb_for_examcode->{$map} = $ressymb;
+ }
+ }
+ }
} else {
$resource_error = 1;
last;
@@ -8752,6 +8805,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) {
@@ -8935,6 +8999,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) {
@@ -9076,10 +9151,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 '';
@@ -9087,7 +9173,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 '';
@@ -9242,11 +9328,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'}) {
@@ -9561,7 +9652,7 @@ END
my @lines = &Apache::lonnet::get_scantronformat_file();
my $count = 0;
foreach my $line (@lines) {
- next if ($line =~ /^#/);
+ next if (($line =~ /^\#/) || ($line eq ''));
$singleline = $line;
$count ++;
}
@@ -9755,7 +9846,7 @@ sub validate_uploaded_scantron_file {
my %unique_formats;
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];
@@ -10125,6 +10216,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);
@@ -11879,8 +11981,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).