--- loncom/homework/grades.pm 2023/07/05 23:49:18 1.596.2.12.2.60.2.4
+++ loncom/homework/grades.pm 2024/12/09 02:29:37 1.797
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.596.2.12.2.60.2.4 2023/07/05 23:49:18 raeburn Exp $
+# $Id: grades.pm,v 1.797 2024/12/09 02:29:37 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -44,12 +44,15 @@ use Apache::Constants qw(:common :http);
use Apache::lonlocal;
use Apache::lonenc;
use Apache::lonstathelpers;
+use Apache::lonquickgrades;
use Apache::bridgetask();
use Apache::lontexconvert();
+use Apache::loncourserespicker;
use String::Similarity;
use HTML::Parser();
use File::MMagic;
use LONCAPA;
+use LONCAPA::ltiutils();
use POSIX qw(floor);
@@ -146,7 +149,6 @@ 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 {
@@ -636,7 +638,7 @@ COMMONJSFUNCTIONS
#--- Dumps the class list with usernames,list of sections,
#--- section, ids and fullnames for each user.
sub getclasslist {
- my ($getsec,$filterbyaccstatus,$getgroup,$symb,$submitonly,$filterbysubmstatus) = @_;
+ my ($getsec,$filterbyaccstatus,$getgroup,$symb,$submitonly,$filterbysubmstatus,$filterbypbid,$possibles) = @_;
my @getsec;
my @getgroup;
my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
@@ -664,12 +666,16 @@ sub getclasslist {
#
my %sections;
my %fullnames;
+ my %passback;
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);
+ } elsif ($filterbypbid) {
+ $cdom = $env{"course.$env{'request.course.id'}.domain"};
+ $cnum = $env{"course.$env{'request.course.id'}.num"};
}
foreach my $student (keys(%$classlist)) {
my $end =
@@ -756,6 +762,27 @@ sub getclasslist {
}
}
}
+ if ($filterbypbid) {
+ if (ref($possibles) eq 'HASH') {
+ unless (exists($possibles->{$student})) {
+ delete($classlist->{$student});
+ next;
+ }
+ }
+ my $udom =
+ $classlist->{$student}->[&Apache::loncoursedata::CL_SDOM()];
+ my $uname =
+ $classlist->{$student}->[&Apache::loncoursedata::CL_SNAME()];
+ if (($udom ne '') && ($uname ne '')) {
+ my %pbinfo = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',[$filterbypbid],$udom,$uname);
+ if (ref($pbinfo{$filterbypbid}) eq 'ARRAY') {
+ $passback{$student} = $pbinfo{$filterbypbid}
+ } else {
+ delete($classlist->{$student});
+ next;
+ }
+ }
+ }
$section = ($section ne '' ? $section : 'none');
if (&canview($section)) {
if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {
@@ -771,7 +798,7 @@ sub getclasslist {
}
}
my @sections = sort(keys(%sections));
- return ($classlist,\@sections,\%fullnames);
+ return ($classlist,\@sections,\%fullnames,\%passback);
}
sub canmodify {
@@ -1034,6 +1061,611 @@ sub verifyreceipt {
return $string;
}
+sub initialpassback {
+ my ($request,$symb) = @_;
+ my $cdom = $env{"course.$env{'request.course.id'}.domain"};
+ my $cnum = $env{"course.$env{'request.course.id'}.num"};
+ my $crstype = &Apache::loncommon::course_type();
+ my %passback = &Apache::lonnet::dump('nohist_linkprot_passback',$cdom,$cnum);
+ my $readonly;
+ unless ($perm{'mgr'}) {
+ $readonly = 1;
+ }
+ my $formname = 'initialpassback';
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ my $output;
+ if (!defined($navmap)) {
+ if ($crstype eq 'Community') {
+ $output = &mt('Unable to retrieve information about community contents');
+ } else {
+ $output = &mt('Unable to retrieve information about course contents');
+ }
+ return '
'.$output.'
';
+ }
+ return &Apache::loncourserespicker::create_picker($navmap,'passback',$formname,$crstype,undef,
+ undef,undef,undef,undef,undef,undef,
+ \%passback,$readonly);
+}
+
+sub passback_filters {
+ my ($request,$symb) = @_;
+ my $cdom = $env{"course.$env{'request.course.id'}.domain"};
+ my $cnum = $env{"course.$env{'request.course.id'}.num"};
+ my $crstype = &Apache::loncommon::course_type();
+ my ($launcher,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);
+ if ($env{'form.passback'} ne '') {
+ $chosen = &unescape($env{'form.passback'});
+ ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
+ ($launcher,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);
+ }
+ my $result;
+ if ($launcher ne '') {
+ $result = &launcher_info_box($launcher,$appname,$setter,$linkuri,$scope).
+ '
'.&mt('Set criteria to use to list students for possible passback of scores, then push Next [_1]',
+ '→').
+ '
';
+ }
+ $result .= ''."\n";
+ return $result;
+}
+
+sub names_for_passback {
+ my ($request,$symb) = @_;
+ my $cdom = $env{"course.$env{'request.course.id'}.domain"};
+ my $cnum = $env{"course.$env{'request.course.id'}.num"};
+ my $crstype = &Apache::loncommon::course_type();
+ my ($launcher,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);
+ if ($env{'form.passback'} ne '') {
+ $chosen = &unescape($env{'form.passback'});
+ ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
+ ($launcher,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);
+ }
+ my ($result,$ctr,$newcommand,$submittext);
+ if ($launcher ne '') {
+ $result = &launcher_info_box($launcher,$appname,$setter,$linkuri,$scope);
+ }
+ $ctr = 0;
+ my @statuses = &Apache::loncommon::get_env_multiple('form.Status');
+ my $stu_status = join(':',@statuses);
+ $result .= ''."\n";
+ return $result;
+}
+
+sub do_passback {
+ my ($request,$symb) = @_;
+ my $cdom = $env{"course.$env{'request.course.id'}.domain"};
+ my $cnum = $env{"course.$env{'request.course.id'}.num"};
+ my $crstype = &Apache::loncommon::course_type();
+ my ($launcher,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);
+ if ($env{'form.passback'} ne '') {
+ $chosen = &unescape($env{'form.passback'});
+ ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
+ ($launcher,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);
+ }
+ if ($launcher ne '') {
+ $request->print(&launcher_info_box($launcher,$appname,$setter,$linkuri,$scope));
+ }
+ my $error;
+ if ($perm{'mgr'}) {
+ if ($launcher ne '') {
+ my @poss_students = &Apache::loncommon::get_env_multiple('form.stuinfo');
+ if (@poss_students) {
+ my %possibles;
+ foreach my $item (@poss_students) {
+ my ($stuname,$studom) = split(/:/,$item,3);
+ $possibles{$stuname.':'.$studom} = 1;
+ }
+ my ($sections,$groups,$group_display,$disabled) = §ions_and_groups();
+ my ($classlist,undef,$fullname,$pbinfo) =
+ &getclasslist($sections,'1',$groups,'','','',$chosen,\%possibles);
+ if ((ref($classlist) eq 'HASH') && (ref($pbinfo) eq 'HASH')) {
+ my %passback = %{$pbinfo};
+ my (%tosend,%remotenotok,%scorenotok,%zeroposs,%nopbinfo);
+ foreach my $possible (keys(%possibles)) {
+ if ((exists($classlist->{$possible})) &&
+ (exists($passback{$possible})) && (ref($passback{$possible}) eq 'ARRAY')) {
+ $tosend{$possible} = 1;
+ }
+ }
+ if (keys(%tosend)) {
+ my ($lti_in_use,$crsdef);
+ my ($ltinum,$ltitype) = ($linkprotector =~ /^(\d+)(c|d)$/);
+ if ($ltitype eq 'c') {
+ my %crslti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
+ $lti_in_use = $crslti{$ltinum};
+ $crsdef = 1;
+ } else {
+ my %domlti = &Apache::lonnet::get_domain_lti($cdom,'linkprot');
+ $lti_in_use = $domlti{$ltinum};
+ }
+ if (ref($lti_in_use) eq 'HASH') {
+ my $msgformat = $lti_in_use->{'passbackformat'};
+ my $keynum = $lti_in_use->{'cipher'};
+ my $scoretype = 'decimal';
+ if ($lti_in_use->{'scoreformat'} =~ /^(decimal|ratio|percentage)$/) {
+ $scoretype = $1;
+ }
+ my $pbsymb = &Apache::loncommon::symb_from_tinyurl($linkuri,$cnum,$cdom);
+ my $pbmap;
+ if ($pbsymb =~ /\.(page|sequence)$/) {
+ $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pbsymb))[2]);
+ } else {
+ $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pbsymb))[0]);
+ }
+ $pbmap = &Apache::lonnet::clutter($pbmap);
+ my $pbscope;
+ if ($scope eq 'res') {
+ $pbscope = 'resource';
+ } elsif ($scope eq 'map') {
+ $pbscope = 'nonrec';
+ } elsif ($scope eq 'rec') {
+ $pbscope = 'map';
+ }
+ my $sigmethod = 'HMAC-SHA1';
+ my $type = 'linkprot';
+ my $clientip = &Apache::lonnet::get_requestor_ip();
+ my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
+ my $ip = &Apache::lonnet::get_host_ip($lonhost);
+ my $numstudents = scalar(keys(%tosend));
+ my %prog_state = &Apache::lonhtmlcommon::Create_PrgWin($request,$numstudents);
+ my $outcome = &Apache::loncommon::start_data_table().
+ &Apache::loncommon::start_data_table_header_row();
+ my $loop = 0;
+ while ($loop < 2) {
+ $outcome .= '
');
+ }
+ if ($noconfirm) {
+ $request->print(' '.&mt('Score receipt not confirmed by receiving CMS').':'.
+ '
'.$noconfirm.'
');
+ }
+ if ($noscore) {
+ $request->print(' '.&mt('Score computation or transmission failed').':'.
+ '
'.$noscore.'
');
+ }
+ $request->print('');
+ }
+ } else {
+ $error = &mt('Settings for deep-link launch target unavailable, so no scores were sent');
+ }
+ } else {
+ $error = &mt('No available students for whom scores can be sent.');
+ }
+ } else {
+ $error = &mt('Classlist could not be retrieved so no scores were sent.');
+ }
+ } else {
+ $error = &mt('No students selected to receive scores so none were sent.');
+ }
+ } else {
+ if ($env{'form.passback'}) {
+ $error = &mt('Deep-link launch target was invalid so no scores were sent.');
+ } else {
+ $error = &mt('Deep-link launch target was missing so no scores were sent.');
+ }
+ }
+ } else {
+ $error = &mt('You do not have permission to manage grades, so no scores were sent');
+ }
+ if ($error) {
+ $request->print('
'.$error.'
');
+ }
+ return;
+}
+
+sub get_passback_launcher {
+ my ($cdom,$cnum,$chosen) = @_;
+ my ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
+ my ($ltinum,$ltitype) = ($linkprotector =~ /^(\d+)(c|d)$/);
+ my ($appname,$setter);
+ if ($ltitype eq 'c') {
+ my %lti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
+ if (ref($lti{$ltinum}) eq 'HASH') {
+ $appname = $lti{$ltinum}{'name'};
+ if ($appname) {
+ $setter = ' (defined in course)';
+ }
+ }
+ } elsif ($ltitype eq 'd') {
+ my %lti = &Apache::lonnet::get_domain_lti($cdom,'linkprot');
+ if (ref($lti{$ltinum}) eq 'HASH') {
+ $appname = $lti{$ltinum}{'name'};
+ if ($appname) {
+ $setter = ' (defined in domain)';
+ }
+ }
+ }
+ if ($linkuri =~ m{^\Q/tiny/$cdom/\E(\w+)$}) {
+ my $key = $1;
+ my $tinyurl;
+ my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key);
+ if (defined($cached)) {
+ $tinyurl = $result;
+ } else {
+ my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
+ my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname);
+ if ($currtiny{$key} ne '') {
+ $tinyurl = $currtiny{$key};
+ &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600);
+ }
+ }
+ if ($tinyurl) {
+ my ($crsnum,$launchsymb) = split(/\&/,$tinyurl);
+ if ($crsnum eq $cnum) {
+ my %passback = &Apache::lonnet::get('nohist_linkprot_passback',[$launchsymb],$cdom,$cnum);
+ if (ref($passback{$launchsymb}) eq 'HASH') {
+ if (exists($passback{$launchsymb}{$chosen})) {
+ return ($launchsymb,$appname,$setter)
+ }
+ }
+ }
+ }
+ }
+ return ();
+}
+
+sub sections_and_groups {
+ my (@sections,@groups,$group_display);
+ @groups = &Apache::loncommon::get_env_multiple('form.group');
+ if (grep(/^all$/,@groups)) {
+ @groups = ('all');
+ $group_display = 'all';
+ } elsif (grep(/^none$/,@groups)) {
+ @groups = ('none');
+ $group_display = 'none';
+ } elsif (@groups > 0) {
+ $group_display = join(', ',@groups);
+ }
+ if ($env{'request.course.sec'} ne '') {
+ @sections = ($env{'request.course.sec'});
+ } else {
+ @sections = &Apache::loncommon::get_env_multiple('form.section');
+ }
+ 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');
+ }
+ return(\@sections,\@groups,$group_display,$disabled);
+}
+
+sub launcher_info_box {
+ my ($launcher,$appname,$setter,$linkuri,$scope) = @_;
+ my $shownscope;
+ if ($scope eq 'res') {
+ $shownscope = &mt('Resource');
+ } elsif ($scope eq 'map') {
+ $shownscope = &mt('Folder');
+ } elsif ($scope eq 'rec') {
+ $shownscope = &mt('Folder + sub-folders');
+ }
+ return '
'."\n";
+}
+
#--- This is called by a number of programs.
#--- Called from the Grading Menu - View/Grade an individual student
#--- Also called directly when one clicks on the subm button
@@ -1065,34 +1697,8 @@ sub listStudents {
}
}
- 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.',
- );
- &js_escape(\%js_lt);
+ $request->print(&checkselect_js());
$request->print(&Apache::lonhtmlcommon::scripttag(< 1) {
- for (var i=0; i 'Please select a student or group of students before pushing the Save Scores button.',
+ 'single' => 'Please select the student before pushing the Save Scores button.',
+ );
+ } else {
+ %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.',
+ );
+ }
+ &js_escape(\%js_lt);
+ return &Apache::lonhtmlcommon::scripttag(< 1) {
+ for (var i=0; i");
pDoc.write("<\\/td><\\/tr><\\/table> ");
pDoc.write(" ");
pDoc.write("
");
@@ -2118,7 +2773,7 @@ sub handback_box {
if ($file =~ /\/portfolio\//) {
$file_counter++;
my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|);
- my ($name,$version,$ext) = &file_name_version_ext($file_disp);
+ my ($name,$version,$ext) = &Apache::lonnet::file_name_version_ext($file_disp);
$file_disp = "$name.$ext";
$file = $file_path.$file_disp;
$result.=&mt('Return commented version of [_1] to student.',
@@ -2330,7 +2985,7 @@ sub submission {
}
if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }
- unless ($is_tool) {
+ unless ($is_tool) {
if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }
if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }
}
@@ -2557,182 +3212,19 @@ sub submission {
# (3) All transactions (by date)
# (4) The whole record (with detailed information for all transactions)
- my ($string,$timestamp,$lastgradetime,$lastsubmittime) = &get_last_submission(\%record,$is_tool);
-
- my $lastsubonly;
-
- if ($timestamp eq '') {
- $lastsubonly.='
';
- 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.')').
- '
';
- } elsif ($ocrsid ne '') {
- my %old_course_desc;
- 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.')').
- '
';
+ 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.')').
+ '
'.
+ ''.&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);
@@ -2944,10 +3616,15 @@ sub get_last_submission {
if ($solved{$partid} ne '') {
$prevsolved{$partid} = $solved{$partid};
}
- }
- $timestamp =
- &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});
+ }
}
+#
+# Timestamp is for last transaction for this resource, which does not
+# necessarily correspond to the time of last submission for problem (or part).
+#
+ if ($lasthash{'timestamp'} ne '') {
+ $timestamp = &Apache::lonlocal::locallocaltime($lasthash{'timestamp'});
+ }
my (%typeparts,%randombytry);
my $showsurv =
&Apache::lonnet::allowed('vas',$env{'request.course.id'});
@@ -3040,11 +3717,12 @@ sub show_previous_task_version {
my ($uname,$udom) = ($env{'form.student'},$env{'form.userdom'});
my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});
if (!&canview($usec)) {
- $request->print(''.
- &mt('Unable to view previous version for requested student.').
- ' '.&mt('([_1] in section [_2] in course id [_3])',
- $uname.':'.$udom,$usec,$env{'request.course.id'}).
- '');
+ $request->print(
+ ''.
+ &mt('Unable to view previous version for requested student.').
+ ' '.&mt('([_1] in section [_2] in course id [_3])',
+ $uname.':'.$udom,$usec,$env{'request.course.id'}).
+ '');
return;
}
my $mode = 'both';
@@ -3246,7 +3924,7 @@ sub processHandGrade {
next;
}
if ($errorflag eq 'not_allowed') {
- $request->print(
+ $request->print(
''
.&mt('Not allowed to modify grades for [_1]',"$uname:$udom")
.'');
@@ -3347,9 +4025,9 @@ 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++;
}
@@ -3357,8 +4035,8 @@ sub processHandGrade {
$keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};
}
if (($numessay) || ($env{'form.compmsg'})) {
- my $putresult = &Apache::lonnet::put
- ('nohist_handgrade',\%keyhash,$cdom,$cnum);
+ my $putresult = &Apache::lonnet::put
+ ('nohist_handgrade',\%keyhash,$cdom,$cnum);
}
# Called by Save & Refresh from Highlight Attribute Window
@@ -3460,7 +4138,7 @@ sub processHandGrade {
$ctr++;
}
if ($total < 0) {
- my $the_end.='
'.&mt('[_1]Message:[_2] No more students for this section or class.','','').'
'."\n";
+ my $the_end.='
'.&mt('[_1]Message:[_2] No more students for this section or class.','','').'
'."\n";
$request->print($the_end);
}
return '';
@@ -3656,19 +4334,19 @@ sub handback_files {
my $part_resp = join('_',@{ $part_response_id });
if (($env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'} =~ /^\d+$/) & ($new_part eq $part_id)) {
for (my $counter=1; $counter<=$env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'}; $counter++) {
- # if multiple files are uploaded names will be 'returndoc2','returndoc3'
- if ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter}) {
+ # if multiple files are uploaded names will be 'returndoc2','returndoc3'
+ if ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter}) {
my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter.'.filename'};
my ($directory,$answer_file) =
($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter} =~ /^(.*?)([^\/]*)$/);
my ($answer_name,$answer_ver,$answer_ext) =
- &file_name_version_ext($answer_file);
+ &Apache::lonnet::file_name_version_ext($answer_file);
my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);
my $getpropath = 1;
my ($dir_list,$listerror) =
&Apache::lonnet::dirlist($portfolio_root.$portfolio_path,
$domain,$stuname,$getpropath);
- my $version = &get_next_version($answer_name,$answer_ext,$dir_list);
+ my $version = &Apache::lonnet::get_next_version($answer_name,$answer_ext,$dir_list);
# fix filename
my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);
my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain,
@@ -3686,8 +4364,7 @@ sub handback_files {
$$newrecord{"resource.$new_part.$resp_id.handback"}.=',';
}
$$newrecord{"resource.$new_part.$resp_id.handback"} .= $save_file_name;
- $file_msg.=''.$save_file_name." ";
-
+ $file_msg.= ''.$save_file_name." ";
}
$request->print(' '.&mt('[_1] will be the uploaded filename [_2]',''.$fname.'',''.$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter}.''));
}
@@ -3698,7 +4375,7 @@ sub handback_files {
$request->print(' ');
my @what = ($symb,$env{'request.course.id'},'handback');
&Apache::lonnet::mark_as_readonly($domain,$stuname,\@handedback,\@what);
- my $user_lh = &Apache::loncommon::user_lang($stuname,$domain,$env{'request.course.id'});
+ my $user_lh = &Apache::loncommon::user_lang($stuname,$domain,$env{'request.course.id'});
my ($subject,$message);
if (scalar(@handedback) == 1) {
$subject = &mt_user($user_lh,'File Handed Back by Instructor');
@@ -3818,93 +4495,20 @@ sub version_portfiles {
my $version_parts = join('|',@$v_flag);
my @returned_keys;
my $parts = join('|', @$parts_graded);
- my $portfolio_root = '/userfiles/portfolio';
foreach my $key (keys(%$record)) {
my $new_portfiles;
if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {
my @versioned_portfiles;
my @portfiles = split(/\s*,\s*/,$$record{$key});
- foreach my $file (@portfiles) {
- &Apache::lonnet::unmark_as_readonly($domain,$stu_name,[$symb,$env{'request.course.id'}],$file);
- my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);
- my ($answer_name,$answer_ver,$answer_ext) =
- &file_name_version_ext($answer_file);
- my $getpropath = 1;
- my ($dir_list,$listerror) =
- &Apache::lonnet::dirlist($portfolio_root.$directory,$domain,
- $stu_name,$getpropath);
- my $version = &get_next_version($answer_name,$answer_ext,$dir_list);
- my $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version);
- if ($new_answer ne 'problem getting file') {
- push(@versioned_portfiles, $directory.$new_answer);
- &Apache::lonnet::mark_as_readonly($domain,$stu_name,
- [$directory.$new_answer],
- [$symb,$env{'request.course.id'},'graded']);
- }
+ if (@portfiles) {
+ &Apache::lonnet::portfiles_versioning($symb,$domain,$stu_name,\@portfiles,
+ \@versioned_portfiles);
}
$$record{$key} = join(',',@versioned_portfiles);
push(@returned_keys,$key);
}
- }
- return (@returned_keys);
-}
-
-sub get_next_version {
- my ($answer_name, $answer_ext, $dir_list) = @_;
- my $version;
- if (ref($dir_list) eq 'ARRAY') {
- foreach my $row (@{$dir_list}) {
- my ($file) = split(/\&/,$row,2);
- my ($file_name,$file_version,$file_ext) =
- &file_name_version_ext($file);
- if (($file_name eq $answer_name) &&
- ($file_ext eq $answer_ext)) {
- # gets here if filename and extension match,
- # regardless of version
- if ($file_version ne '') {
- # a versioned file is found so save it for later
- if ($file_version > $version) {
- $version = $file_version;
- }
- }
- }
- }
}
- $version ++;
- return($version);
-}
-
-sub version_selected_portfile {
- my ($domain,$stu_name,$directory,$file_name,$version) = @_;
- my ($answer_name,$answer_ver,$answer_ext) =
- &file_name_version_ext($file_name);
- my $new_answer;
- $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stu_name/portfolio$directory$file_name");
- if($env{'form.copy'} eq '-1') {
- $new_answer = 'problem getting file';
- } else {
- $new_answer = $answer_name.'.'.$version.'.'.$answer_ext;
- my $copy_result = &Apache::lonnet::finishuserfileupload(
- $stu_name,$domain,'copy',
- '/portfolio'.$directory.$new_answer);
- }
- return ($new_answer);
-}
-
-sub file_name_version_ext {
- my ($file)=@_;
- my @file_parts = split(/\./, $file);
- my ($name,$version,$ext);
- if (@file_parts > 1) {
- $ext=pop(@file_parts);
- if (@file_parts > 1 && $file_parts[-1] =~ /^\d+$/) {
- $version=pop(@file_parts);
- }
- $name=join('.',@file_parts);
- } else {
- $name=join('.',@file_parts);
- }
- return($name,$version,$ext);
+ return (@returned_keys);
}
#--------------------------------------------------------------------------------------
@@ -4152,7 +4756,7 @@ sub viewgrades {
$common_header = &mt('Assign Common Grade to Students not assigned to any groups');
$specific_header = &mt('Assign Grade to Specific Students not assigned to any groups');
} else {
- $common_header = &mt('Assign Common Grade to Class');
+ $common_header = &mt('Assign Common Grade to Class');
$specific_header = &mt('Assign Grade to Specific Students in Class');
}
} elsif (grep(/^none$/,@sections)) {
@@ -4165,7 +4769,7 @@ sub viewgrades {
$specific_header = &mt('Assign Grade to Specific Students in no Section and in no Group');
} else {
$common_header = &mt('Assign Common Grade to Students in no Section');
- $specific_header = &mt('Assign Grade to Specific Students in no Section');
+ $specific_header = &mt('Assign Grade to Specific Students in no Section');
}
} else {
$section_display = join (", ",@sections);
@@ -4179,7 +4783,7 @@ sub viewgrades {
$specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1] and no Group',$section_display);
} else {
$common_header = &mt('Assign Common Grade to Students in Section(s) [_1]',$section_display);
- $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1]',$section_display);
+ $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1]',$section_display);
}
}
my %submit_types = &substatus_options();
@@ -4236,10 +4840,10 @@ sub viewgrades {
$partid.'" size="4" '.'onchange="javascript:writePoint(\''.
$partid.'\','.$weight{$partid}.',\'textval\')" /> /'.
$weight{$partid}.' '.&mt('(problem weight)').''."\n";
- $line.= '
'.&mt('Grade Status').':'.
- '
'.
&Apache::loncommon::end_data_table_row().
&Apache::loncommon::end_data_table();
return $result;
@@ -5001,13 +5605,12 @@ sub csvuploadmap {
if (!$env{'form.datatoken'}) {
$datatoken=&Apache::loncommon::upfile_store($request);
} else {
- $datatoken=&Apache::loncommon::valid_datatoken($env{'form.datatoken'});
- if ($datatoken ne '') {
+ $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();
- if ($env{'form.noFirstLine'}) { shift(@records); }
&csvuploadmap_header($request,$symb,$datatoken,$#records+1);
my ($i,$keyfields);
if (@records) {
@@ -5044,8 +5647,6 @@ sub csvuploadmap {
sub csvuploadoptions {
my ($request,$symb)= @_;
my $overwrite=&mt('Overwrite any existing score');
- my $checked=(($env{'form.noFirstLine'})?'1':'0');
- my $ignore=&mt('Ignore First Line');
$request->print(<
@@ -5059,7 +5660,7 @@ ENDPICK
my %fields=&get_fields();
if (!defined($fields{'domain'})) {
my $domform = &Apache::loncommon::select_dom_form($env{'request.role.domain'},'default_domain');
- $request->print("\n
".&mt('Users are in domain: [_1]',$domform)."
\n");
+ $request->print("\n
".&mt('Users are in domain: [_1]',$domform)."
\n");
}
foreach my $key (sort(keys(%env))) {
if ($key !~ /^form\.(.*)$/) { next; }
@@ -5097,11 +5698,10 @@ sub csvuploadassign {
if (!$symb) {return '';}
my $error_msg = '';
my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'});
- if ($datatoken ne '') {
+ if ($datatoken ne '') {
&Apache::loncommon::load_tmp_file($request,$datatoken);
}
my @gradedata = &Apache::loncommon::upfile_record_sep();
- if ($env{'form.noFirstLine'}) { shift(@gradedata); }
my %fields=&get_fields();
my $courseid=$env{'request.course.id'};
my ($classlist) = &getclasslist('all',0);
@@ -5123,13 +5723,45 @@ sub csvuploadassign {
if (!$username) {
my $id=$entries{$fields{'ID'}};
$id=~s/\s//g;
- my %ids=&Apache::lonnet::idget($domain,$id);
- $username=$ids{$id};
+ if ($id ne '') {
+ my %ids=&Apache::lonnet::idget($domain,[$id]);
+ $username=$ids{$id};
+ } else {
+ if ($entries{$fields{'clicker'}}) {
+ my $clicker = $entries{$fields{'clicker'}};
+ $clicker=~s/\s//g;
+ if ($clicker ne '') {
+ my %clickers = &Apache::lonnet::idget($domain,[$clicker],'clickers');
+ if ($clickers{$clicker} ne '') {
+ my $match = 0;
+ my @inclass;
+ foreach my $poss (split(/,/,$clickers{$clicker})) {
+ if (exists($$classlist{"$poss:$domain"})) {
+ $username = $poss;
+ push(@inclass,$poss);
+ $match ++;
+
+ }
+ }
+ if ($match > 1) {
+ undef($username);
+ $request->print('
'.
+ &mt('Score not saved for clicker: [_1] (matched multiple usernames: [_2])',
+ $clicker,join(', ',@inclass)).'
');
+ }
+ }
+ }
+ }
+ }
}
if (!exists($$classlist{"$username:$domain"})) {
my $id=$entries{$fields{'ID'}};
$id=~s/\s//g;
- if ($id) {
+ my $clicker = $entries{$fields{'clicker'}};
+ $clicker=~s/\s//g;
+ if ($clicker) {
+ push(@skipped,"$clicker:$domain");
+ } elsif ($id) {
push(@skipped,"$id:$domain");
} else {
push(@skipped,"$username:$domain");
@@ -5157,7 +5789,7 @@ sub csvuploadassign {
my $award=($pcr == 0) ? 'incorrect_by_override'
: 'correct_by_override';
if ($pcr>1) {
- push(@warnings,&mt("[_1]: point value larger than weight","$username:$domain"));
+ push(@warnings,&mt("[_1]: point value larger than weight","$username:$domain"));
}
$grades{"resource.$part.awarded"}=$pcr;
$grades{"resource.$part.solved"}=$award;
@@ -5193,13 +5825,13 @@ sub csvuploadassign {
$env{'course.'.$env{'request.course.id'}.'.domain'},
$env{'course.'.$env{'request.course.id'}.'.num'},
$domain,$username);
- } else {
+ $countdone++;
+ } else {
$request->print("
".
&mt("Failed to save data for student [_1]. Message when trying to save was: [_2]",
"$username:$domain",$result)."