--- loncom/homework/grades.pm 2017/12/31 14:00:41 1.749
+++ loncom/homework/grades.pm 2023/04/02 03:16:28 1.793
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.749 2017/12/31 14:00:41 raeburn Exp $
+# $Id: grades.pm,v 1.793 2023/04/02 03:16:28 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -46,7 +46,10 @@ use Apache::lonenc;
use Apache::lonstathelpers;
use Apache::lonquickgrades;
use Apache::bridgetask();
+use Apache::lontexconvert();
use String::Similarity;
+use HTML::Parser();
+use File::MMagic;
use LONCAPA;
use POSIX qw(floor);
@@ -144,7 +147,7 @@ sub nameUserString {
}
#--- Get the partlist and the response type for a given problem. ---
-#--- Indicate if a response type is coded handgraded or not. ---
+#--- Count responseIDs, essayresponse items, and dropbox items ---
#--- Sets response_error pointer to "1" if navmaps object broken ---
sub response_type {
my ($symb,$response_error) = @_;
@@ -162,6 +165,7 @@ sub response_type {
return;
}
my $partlist = $res->parts();
+ my ($numresp,$numessay,$numdropbox) = (0,0,0);
my %vPart =
map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart'));
my (%response_types,%handgrade);
@@ -171,13 +175,20 @@ sub response_type {
my @types = $res->responseType($part);
my @ids = $res->responseIds($part);
for (my $i=0; $i < scalar(@ids); $i++) {
+ $numresp ++;
$response_types{$part}{$ids[$i]} = $types[$i];
+ if ($types[$i] eq 'essay') {
+ $numessay ++;
+ if (&Apache::lonnet::EXT("resource.$part".'_'.$ids[$i].".uploadedfiletypes",$symb)) {
+ $numdropbox ++;
+ }
+ }
$handgrade{$part.'_'.$ids[$i]} =
&Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i].
'.handgrade',$symb);
}
}
- return ($partlist,\%handgrade,\%response_types);
+ return ($partlist,\%handgrade,\%response_types,$numresp,$numessay,$numdropbox);
}
sub flatten_responseType {
@@ -204,6 +215,129 @@ sub get_display_part {
return $display;
}
+#--- Show parts and response type
+sub showResourceInfo {
+ my ($symb,$partlist,$responseType,$formname,$checkboxes,$uploads) = @_;
+ unless ((ref($partlist) eq 'ARRAY') && (ref($responseType) eq 'HASH')) {
+ return ' ';
+ }
+ my $coltitle = &mt('Problem Part Shown');
+ if ($checkboxes) {
+ $coltitle = &mt('Problem Part');
+ } else {
+ my $checkedparts = 0;
+ foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {
+ if (grep(/^\Q$partid\E$/,@{$partlist})) {
+ $checkedparts ++;
+ }
+ }
+ if ($checkedparts == scalar(@{$partlist})) {
+ return ' ';
+ }
+ if ($uploads) {
+ $coltitle = &mt('Problem Part Selected');
+ }
+ }
+ my $result = '
';
+ if ($checkboxes) {
+ my $legend = &mt('Parts to display');
+ if ($uploads) {
+ $legend = &mt('Part(s) with dropbox');
+ }
+ $result .= '';
+ }
+ $result .= '
';
+ if (!keys(%partsseen)) {
+ $result = '';
+ if ($uploads) {
+ return ''.
+ '
'.
+ &mt('No dropbox items or essayresponse items with uploadedfiletypes set.').
+ '
';
+ } else {
+ return ' ';
+ }
+ }
+ return $result;
+}
+
+sub part_selector_js {
+ my $js = <<"END";
+function toggleParts(formname) {
+ if (document.getElementById('LC_partselector')) {
+ var index = '';
+ if (document.forms.length) {
+ for (var i=0; i 1)) {
+ for (var i=0; i $scancode,};
}
}
- my $analyze =
+ my $analyze =
&get_analyze($symb,$uname,$udom,undef,$add_to_form,
undef,undef,undef,$bubbles_per_row);
if (ref($analyze) eq 'HASH') {
@@ -342,7 +476,7 @@ sub cleanRecord {
if ($response =~ /^(option|rank)$/) {
my %answer=&Apache::lonnet::str2hash($answer);
my @answer = %answer;
- %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer;
+ %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer;
my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
my ($toprow,$bottomrow);
foreach my $foil (@$order) {
@@ -360,7 +494,7 @@ sub cleanRecord {
} elsif ($response eq 'match') {
my %answer=&Apache::lonnet::str2hash($answer);
my @answer = %answer;
- %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer;
+ %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer;
my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"});
my ($toprow,$middlerow,$bottomrow);
@@ -417,8 +551,8 @@ sub cleanRecord {
$env{'form.kwstyle'} = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
$env{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.
}
+ $answer = &Apache::lontexconvert::msgtexconverted($answer);
return '
'.&keywords_highlight($answer).'
';
-
} elsif ( $response eq 'organic') {
my $result=&mt('Smile representation: [_1]',
'"'.&HTML::Entities::encode($answer, '"<>&').'"');
@@ -502,7 +636,7 @@ COMMONJSFUNCTIONS
#--- Dumps the class list with usernames,list of sections,
#--- section, ids and fullnames for each user.
sub getclasslist {
- my ($getsec,$filterlist,$getgroup) = @_;
+ my ($getsec,$filterbyaccstatus,$getgroup,$symb,$submitonly,$filterbysubmstatus) = @_;
my @getsec;
my @getgroup;
my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
@@ -530,6 +664,13 @@ sub getclasslist {
#
my %sections;
my %fullnames;
+ my ($cdom,$cnum,$partlist);
+ if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {
+ $cdom = $env{"course.$env{'request.course.id'}.domain"};
+ $cnum = $env{"course.$env{'request.course.id'}.num"};
+ my $res_error;
+ ($partlist) = &response_type($symb,\$res_error);
+ }
foreach my $student (keys(%$classlist)) {
my $end =
$classlist->{$student}->[&Apache::loncoursedata::CL_END()];
@@ -546,7 +687,7 @@ sub getclasslist {
my $group =
$classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
# filter students according to status selected
- if ($filterlist && (!($stu_status =~ /Any/))) {
+ if ($filterbyaccstatus && (!($stu_status =~ /Any/))) {
if (!($stu_status =~ $status)) {
delete($classlist->{$student});
next;
@@ -563,13 +704,58 @@ sub getclasslist {
}
}
if (($grp eq 'none') && !$group) {
- $exclude = 0;
+ $exclude = 0;
}
}
if ($exclude) {
delete($classlist->{$student});
+ next;
}
}
+ if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {
+ my $udom =
+ $classlist->{$student}->[&Apache::loncoursedata::CL_SDOM()];
+ my $uname =
+ $classlist->{$student}->[&Apache::loncoursedata::CL_SNAME()];
+ if (($symb ne '') && ($udom ne '') && ($uname ne '')) {
+ if ($submitonly eq 'queued') {
+ my %queue_status =
+ &Apache::bridgetask::get_student_status($symb,$cdom,$cnum,
+ $udom,$uname);
+ if (!defined($queue_status{'gradingqueue'})) {
+ delete($classlist->{$student});
+ next;
+ }
+ } else {
+ my (%status) =&student_gradeStatus($symb,$udom,$uname,$partlist);
+ my $submitted = 0;
+ my $graded = 0;
+ my $incorrect = 0;
+ foreach (keys(%status)) {
+ $submitted = 1 if ($status{$_} ne 'nothing');
+ $graded = 1 if ($status{$_} =~ /^ungraded/);
+ $incorrect = 1 if ($status{$_} =~ /^incorrect/);
+
+ my ($foo,$partid,$foo1) = split(/\./,$_);
+ if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
+ $submitted = 0;
+ }
+ }
+ if (!$submitted && ($submitonly eq 'yes' ||
+ $submitonly eq 'incorrect' ||
+ $submitonly eq 'graded')) {
+ delete($classlist->{$student});
+ next;
+ } elsif (!$graded && ($submitonly eq 'graded')) {
+ delete($classlist->{$student});
+ next;
+ } elsif (!$incorrect && $submitonly eq 'incorrect') {
+ delete($classlist->{$student});
+ next;
+ }
+ }
+ }
+ }
$section = ($section ne '' ? $section : 'none');
if (&canview($section)) {
if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {
@@ -584,7 +770,6 @@ sub getclasslist {
delete($classlist->{$student});
}
}
- my %seen = ();
my @sections = sort(keys(%sections));
return ($classlist,\@sections,\%fullnames);
}
@@ -600,7 +785,7 @@ sub canmodify {
#can modify the requested section
return 1;
} else {
- # can't modify the request section
+ # can't modify the requested section
return 0;
}
}
@@ -613,19 +798,19 @@ sub canview {
my ($sec)=@_;
if ($perm{'vgr'}) {
if (!defined($perm{'vgr_section'})) {
- # can modify whole class
+ # can view whole class
return 1;
} else {
if ($sec eq $perm{'vgr_section'}) {
- #can modify the requested section
+ #can view the requested section
return 1;
} else {
- # can't modify the request section
+ # can't view the requested section
return 0;
}
}
}
- #can't modify
+ #can't view
return 0;
}
@@ -766,14 +951,14 @@ sub initialverifyreceipt {
#--- Check whether a receipt number is valid.---
sub verifyreceipt {
- my ($request,$symb) = @_;
+ my ($request,$symb) = @_;
my $courseid = $env{'request.course.id'};
my $receipt = &Apache::lonnet::recprefix($courseid).'-'.
$env{'form.receipt'};
$receipt =~ s/[^\-\d]//g;
- my $title.=
+ my $title =
'
'.
&mt('Verifying Receipt Number [_1]',$receipt).
'
'."\n";
@@ -854,7 +1039,7 @@ sub verifyreceipt {
#--- Also called directly when one clicks on the subm button
# on the problem page.
sub listStudents {
- my ($request,$symb,$submitonly) = @_;
+ my ($request,$symb,$submitonly,$divforres) = @_;
my $is_tool = ($symb =~ /ext\.tool$/);
my $cdom = $env{"course.$env{'request.course.id'}.domain"};
@@ -862,12 +1047,23 @@ sub listStudents {
my $getsec = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
my $getgroup = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
unless ($submitonly) {
- $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
+ $submitonly = $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
}
my $result='';
my $res_error;
- my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
+ my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,\$res_error);
+
+ my $table;
+ if (ref($partlist) eq 'ARRAY') {
+ if (scalar(@$partlist) > 1 ) {
+ $table = &showResourceInfo($symb,$partlist,$responseType,'gradesub',1);
+ } elsif ($divforres) {
+ $table = '';
+ } else {
+ $table = ' ';
+ }
+ }
my %js_lt = &Apache::lonlocal::texthash (
'multiple' => 'Please select a student or group of students before clicking on the Next button.',
@@ -909,8 +1105,8 @@ LISTJAVASCRIPT
$request->print($result);
my $gradeTable=''."\n";
$request->print($studentTable);
@@ -5167,11 +5612,11 @@ sub displaySubByDates {
}
my @matchKey;
if ($isTask) {
- @matchKey = sort(grep /^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys);
+ @matchKey = sort(grep(/^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys));
} elsif ($is_tool) {
- @matchKey = sort(grep /^resource\.\Q$partid\E\.awarded$/,@versionKeys);
+ @matchKey = sort(grep(/^resource\.\Q$partid\E\.awarded$/,@versionKeys));
} else {
- @matchKey = sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys);
+ @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);
@@ -5337,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);
@@ -5346,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;
@@ -5412,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) {
@@ -5511,7 +5970,7 @@ the homework problem.
sub defaultFormData {
my ($symb)=@_;
- return '';
+ return '';
}
@@ -5664,7 +6123,7 @@ sub scantron_uploads {
sub scantron_scantab {
my $result='