--- loncom/homework/grades.pm 2014/01/18 01:57:41 1.596.2.12.2.27
+++ loncom/homework/grades.pm 2022/02/09 23:50:00 1.596.2.12.2.60
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.596.2.12.2.27 2014/01/18 01:57:41 raeburn Exp $
+# $Id: grades.pm,v 1.596.2.12.2.60 2022/02/09 23:50:00 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -43,7 +43,11 @@ use Apache::lonmsg();
use Apache::Constants qw(:common :http);
use Apache::lonlocal;
use Apache::lonenc;
+use Apache::lonstathelpers;
use Apache::bridgetask();
+use Apache::lontexconvert();
+use HTML::Parser();
+use File::MMagic;
use String::Similarity;
use LONCAPA;
@@ -98,6 +102,9 @@ sub ssi_print_error {
#
# --- Retrieve the parts from the metadata file.---
+# Returns an array of everything that the resources stores away
+#
+
sub getpartlist {
my ($symb,$errorref) = @_;
@@ -122,24 +129,6 @@ sub getpartlist {
return @stores;
}
-# --- Get the symbolic name of a problem and the url
-sub get_symb {
- my ($request,$silent) = @_;
- my $symb=$env{'form.symb'};
- unless ($symb) {
- (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
- $symb = &Apache::lonnet::symbread($url);
- if ($symb eq '') {
- if (!$silent) {
- $request->print(&mt("Unable to handle ambiguous references: [_1].",$url));
- return ();
- }
- }
- }
- &Apache::lonenc::check_decrypt(\$symb);
- return ($symb);
-}
-
#--- Format fullname, username:domain if different for display
#--- Use anywhere where the student names are listed
sub nameUserString {
@@ -154,6 +143,8 @@ 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) = @_;
@@ -170,6 +161,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);
@@ -179,13 +171,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 {
@@ -212,53 +211,127 @@ sub get_display_part {
return $display;
}
-#--- Show resource title
-#--- and parts and response type
+#--- Show parts and response type
sub showResourceInfo {
- my ($symb,$probTitle,$checkboxes,$res_error) = @_;
- my $result = '
'.&mt('Current Resource').': '.$probTitle.'
'."\n";
- my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error);
- if (ref($res_error)) {
- if ($$res_error) {
- return;
+ 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; isymb();
@@ -362,6 +435,13 @@ sub reset_caches {
if ($check_for_randomlist) {
$add_to_form = { 'check_parts_withrandomlist' => 1,};
}
+ if ($scancode) {
+ if (ref($add_to_form) eq 'HASH') {
+ $add_to_form->{'code_for_randomlist'} = $scancode;
+ } else {
+ $add_to_form = { 'code_for_randomlist' => $scancode,};
+ }
+ }
my $analyze =
&get_analyze($symb,$uname,$udom,undef,$add_to_form,
undef,undef,undef,$bubbles_per_row);
@@ -391,6 +471,8 @@ sub cleanRecord {
my $grayFont = '';
if ($response =~ /^(option|rank)$/) {
my %answer=&Apache::lonnet::str2hash($answer);
+ my @answer = %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) {
@@ -407,6 +489,8 @@ sub cleanRecord {
$bottomrow.'';
} elsif ($response eq 'match') {
my %answer=&Apache::lonnet::str2hash($answer);
+ my @answer = %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);
@@ -429,6 +513,8 @@ sub cleanRecord {
$bottomrow.'';
} elsif ($response eq 'radiobutton') {
my %answer=&Apache::lonnet::str2hash($answer);
+ my @answer = %answer;
+ %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer;
my ($toprow,$bottomrow);
my $correct =
&get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom,$type,$trial,$rndseed);
@@ -461,10 +547,11 @@ 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 =~ s-\n- -g;
+ $answer = &Apache::lontexconvert::msgtexconverted($answer);
return '
'.&keywords_highlight($answer).'
';
} elsif ( $response eq 'organic') {
- my $result='Smile representation: "'.$answer.'"';
+ my $result=&mt('Smile representation: [_1]',
+ '"'.&HTML::Entities::encode($answer, '"<>&').'"');
my $jme=$record->{$version."resource.$partid.$respid.molecule"};
$result.=&Apache::chemresponse::jme_img($jme,$answer,400);
return $result;
@@ -498,19 +585,20 @@ sub cleanRecord {
$result.='';
return $result;
}
- } elsif ( $response =~ m/(?:numerical|formula)/) {
+ } elsif ( $response =~ m/(?:numerical|formula|custom)/) {
+ # Respect multiple input fields, see Bug #5409
$answer =
&Apache::loncommon::format_previous_attempt_value('submission',
$answer);
+ return $answer;
}
- return $answer;
+ return &HTML::Entities::encode($answer, '"<>&');
}
#-- A couple of common js functions
sub commonJSfunctions {
my $request = shift;
- $request->print(<
+ $request->print(&Apache::lonhtmlcommon::scripttag(< 1) {
@@ -538,14 +626,13 @@ sub commonJSfunctions {
return selectOne.value;
}
}
-
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'));
@@ -573,6 +660,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()];
@@ -589,7 +683,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;
@@ -606,13 +700,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)) {
@@ -627,7 +766,6 @@ sub getclasslist {
delete($classlist->{$student});
}
}
- my %seen = ();
my @sections = sort(keys(%sections));
return ($classlist,\@sections,\%fullnames);
}
@@ -643,7 +781,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;
}
}
@@ -656,19 +794,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;
}
@@ -693,17 +831,15 @@ sub student_gradeStatus {
sub jscriptNform {
my ($symb) = @_;
my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
- my $jscript=''."\n";
+ "\n");
$jscript.= '\n";
+}
+
#--- Check whether a receipt number is valid.---
sub verifyreceipt {
- my $request = shift;
+ my ($request,$symb) = @_;
my $courseid = $env{'request.course.id'};
my $receipt = &Apache::lonnet::recprefix($courseid).'-'.
$env{'form.receipt'};
$receipt =~ s/[^\-\d]//g;
- my ($symb) = &get_symb($request);
- my $title.=
+ my $title =
'
'."\n";
+ &mt('Verifying Receipt Number [_1]',$receipt).
+ ''."\n";
my ($string,$contents,$matches) = ('','',0);
my (undef,undef,$fullname) = &getclasslist('all','0');
@@ -882,7 +1027,7 @@ sub verifyreceipt {
$contents.
&Apache::loncommon::end_data_table()."\n";
}
- return $string.&show_grading_menu_form($symb);
+ return $string;
}
#--- This is called by a number of programs.
@@ -890,30 +1035,37 @@ sub verifyreceipt {
#--- Also called directly when one clicks on the subm button
# on the problem page.
sub listStudents {
- my ($request) = shift;
+ my ($request,$symb,$submitonly,$divforres) = @_;
- my ($symb) = &get_symb($request);
my $cdom = $env{"course.$env{'request.course.id'}.domain"};
my $cnum = $env{"course.$env{'request.course.id'}.num"};
my $getsec = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
my $getgroup = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
- my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
- my $viewgrade = $env{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View';
- $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ?
- &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};
-
- my $result='
'
- .&mt("$viewgrade Submissions for a Student or a Group of Students")
- .'
';
+ unless ($submitonly) {
+ $submitonly = $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
+ }
+
+ my $result='';
+ my $res_error;
+ my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,\$res_error);
- my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));
+ my $table;
+ if (ref($partlist) eq 'ARRAY') {
+ if (scalar(@$partlist) > 1 ) {
+ $table = &showResourceInfo($symb,$partlist,$responseType,'gradesub',1);
+ } elsif ($divforres) {
+ $table = '';
+ } else {
+ $table = ' ';
+ }
+ }
- my %lt = &Apache::lonlocal::texthash (
+ my %js_lt = &Apache::lonlocal::texthash (
'multiple' => 'Please select a student or group of students before clicking on the Next button.',
'single' => 'Please select the student before clicking on the Next button.',
);
- $request->print(<
+ &js_escape(\%js_lt);
+ $request->print(&Apache::lonhtmlcommon::scripttag(<
LISTJAVASCRIPT
&commonJSfunctions($request);
$request->print($result);
- my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked="checked"' : '';
- my $checklastsub = $checkhdgrade eq '' ? 'checked="checked"' : '';
my $gradeTable='