--- loncom/homework/grades.pm 2019/02/06 05:46:51 1.758
+++ loncom/homework/grades.pm 2020/05/20 22:02:57 1.770
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.758 2019/02/06 05:46:51 raeburn Exp $
+# $Id: grades.pm,v 1.770 2020/05/20 22:02:57 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -48,6 +48,8 @@ use Apache::lonquickgrades;
use Apache::bridgetask();
use Apache::lontexconvert();
use String::Similarity;
+use HTML::Parser();
+use File::MMagic;
use LONCAPA;
use POSIX qw(floor);
@@ -313,7 +315,7 @@ sub reset_caches {
$add_to_form = { 'code_for_randomlist' => $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') {
@@ -343,7 +345,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) {
@@ -361,7 +363,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);
@@ -420,7 +422,6 @@ sub cleanRecord {
}
$answer = &Apache::lontexconvert::msgtexconverted($answer);
return '
print(&mt('There are currently no submitted documents.'));
- return;
+ $r->print(&mt('There are currently no submitted documents.'));
+ return;
}
my $all_students =
join("\n", &Apache::loncommon::get_env_multiple('form.stuinfo'));
@@ -4197,7 +4198,7 @@ sub editgrades {
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().
@@ -4640,7 +4641,7 @@ ENDUPFORM
sub csvuploadmap {
- my ($request,$symb)= @_;
+ my ($request,$symb) = @_;
if (!$symb) {return '';}
my $datatoken;
@@ -4736,7 +4737,7 @@ sub get_fields {
}
sub csvuploadassign {
- my ($request,$symb)= @_;
+ my ($request,$symb) = @_;
if (!$symb) {return '';}
my $error_msg = '';
my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'});
@@ -4852,7 +4853,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'}";
@@ -4923,6 +4924,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').'
';
@@ -5012,7 +5014,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
{
@@ -5308,11 +5310,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);
@@ -5652,7 +5654,7 @@ the homework problem.
sub defaultFormData {
my ($symb)=@_;
- return '';
+ return '';
}
@@ -5895,8 +5897,7 @@ sub scantron_selectphase {
$ssi_error = 0;
- if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||
- &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
+ if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'}) {
# Chunk of form to prompt for a scantron file upload.
@@ -5904,6 +5905,7 @@ sub scantron_selectphase {
');
my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
+ my $csec= $env{'request.course.sec'};
my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');
&js_escape(\$alertmsg);
my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($cdom);
@@ -5919,6 +5921,7 @@ sub scantron_selectphase {
';
return $result;
}
@@ -10021,7 +10450,7 @@ sub reset_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))) {
@@ -10209,12 +10638,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().
@@ -10223,7 +10652,7 @@ ENDPERCFORM
}
sub process_clicker_file {
- my ($r,$symb)=@_;
+ my ($r,$symb) = @_;
if (!$symb) {return '';}
my %Saveable_Parameters=&clicker_grading_parameters();
@@ -10295,6 +10724,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
@@ -10321,12 +10766,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).'
'.
@@ -10379,7 +10826,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++;
}
}
@@ -10434,6 +10881,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;
@@ -10492,7 +10982,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;
@@ -10503,11 +10993,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=();
@@ -10569,7 +11059,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 '*') {
@@ -10638,15 +11128,17 @@ sub startpage {
}
if ($nomenu) {
$args{'only_body'} = 1;
- $r->print(&Apache::loncommon::start_page("Student's Version",$js,\%args);
+ $r->print(&Apache::loncommon::start_page("Student's Version",$js,\%args));
} else {
unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});
$args{'bread_crumbs'} = $crumbs;
$r->print(&Apache::loncommon::start_page('Grading',$js,\%args));
- &Apache::lonquickgrades::startGradeScreen($r,($env{'form.symb'}?'probgrading':'grading'));
+ 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));
}
}
@@ -10839,20 +11331,21 @@ 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'}))) {
+ (&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));
@@ -10874,7 +11367,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();
@@ -11112,7 +11605,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() :
|