--- loncom/homework/grades.pm	2019/01/27 14:39:55	1.754
+++ loncom/homework/grades.pm	2020/05/08 15:12:34	1.769
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.754 2019/01/27 14:39:55 raeburn Exp $
+# $Id: grades.pm,v 1.769 2020/05/08 15:12:34 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 '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>';
-
     } elsif ( $response eq 'organic') {
         my $result=&mt('Smile representation: [_1]',
                            '"<tt>'.&HTML::Entities::encode($answer, '"<>&').'</tt>"');
@@ -653,7 +654,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;
 	    }
 	}
@@ -666,19 +667,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;
 }
 
@@ -819,14 +820,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 =
 	'<h3><span class="LC_info">'.
 	&mt('Verifying Receipt Number [_1]',$receipt).
 	'</span></h3>'."\n";
@@ -915,7 +916,7 @@ 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='';
@@ -1212,8 +1213,8 @@ LISTJAVASCRIPT
 #---- Called from the listStudents routine
 
 sub check_script {
-    my ($form, $type)=@_;
-    my $chkallscript= &Apache::lonhtmlcommon::scripttag('
+    my ($form,$type) = @_;
+    my $chkallscript = &Apache::lonhtmlcommon::scripttag('
     function checkall() {
         for (i=0; i<document.forms.'.$form.'.elements.length; i++) {
             ele = document.forms.'.$form.'.elements[i];
@@ -1258,7 +1259,7 @@ sub check_buttons {
 
 #     Displays the submissions for one student or a group of students
 sub processGroup {
-    my ($request,$symb)  = @_;
+    my ($request,$symb) = @_;
     my $ctr        = 0;
     my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');
     my $total      = scalar(@stuchecked)-1;
@@ -2024,8 +2025,8 @@ sub files_exist {
 sub download_all_link {
     my ($r,$symb) = @_;
     unless (&files_exist($r, $symb)) {
-       $r->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='<h2>'.&mt('Current Grade Status').'</h2>';
-    $title.='<h4>'.&mt('<b>Section: </b>[_1]',$section_display).'</h4>'."\n";
+    $title.='<h4><b>'.&mt('Section:').'</b> '.$section_display.'</h4>'."\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='<h3><span class="LC_info">&nbsp;'.
 	&mt('Manual Grading by Page or Sequence').'</span></h3>';
@@ -5012,7 +5014,7 @@ LISTJAVASCRIPT
 	'<th>'.&nameUserString('header').'</th>'.
 	&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 '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />';
+    return '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />';
 }
 
 
@@ -5902,7 +5904,6 @@ sub scantron_selectphase {
 
         $r->print('
     <br />');
-    my $default_form_data=&defaultFormData($symb);
     my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
     my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
     my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');
@@ -5915,7 +5916,7 @@ sub scantron_selectphase {
 	    return false;
 	}
 	formname.submit();
-    }'.$formatjs));
+    }'."\n".$formatjs));
     $r->print('
               <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
                 '.$default_form_data.'
@@ -6000,8 +6001,6 @@ sub scantron_selectphase {
    
     $r->print($result);
 
-
-
     # Chunk of the form that prompts to view a scoring office file,
     # corrected file, skipped records in a file.
 
@@ -7825,7 +7824,7 @@ sub verify_bubbles_checked {
     my $ansnumstr = join('","',@ansnums);
     my $warning = &mt("A bubble or 'No bubble' selection has not been made for one or more lines.");
     &js_escape(\$warning);
-    my $output = &Apache::lonhtmlcommon::scripttag((<<ENDSCRIPT));
+    my $output = &Apache::lonhtmlcommon::scripttag(<<ENDSCRIPT);
 function verify_bubble_radio(form) {
     var ansnumArray = new Array ("$ansnumstr");
     var need_bubble_count = 0;
@@ -8684,7 +8683,7 @@ SCANTRONFORM
 	return '';		# Dunno why the other returns return '' rather than just returning.
     }
 
-    my %lettdig = &letter_to_digits();
+    my %lettdig = &Apache::lonnet::letter_to_digits();
     my $numletts = scalar(keys(%lettdig));
     my %orderedforcode;
 
@@ -9009,7 +9008,7 @@ sub grade_student_bubbles {
 }
 
 sub scantron_upload_scantron_data {
-    my ($r,$symb)=@_;
+    my ($r,$symb) = @_;
     my $dom = $env{'request.role.domain'};
     my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($dom);
     my $domdesc = &Apache::lonnet::domain($dom,'description');
@@ -9099,20 +9098,23 @@ END
             if (keys(%{$domconfig{'scantron'}{'config'}}) > 1) {
                 if (($domconfig{'scantron'}{'config'}{'dat'}) &&
                     (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH')) {
-                    if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}})) {
-                        my ($onclick,$formatextra,$singleline);
-                        my @lines = &Apache::lonnet::get_scantronformat_file();
-                        my $count = 0;
-                        foreach my $line (@lines) {
-                            next if ($line =~ /^#/);
-                            $singleline = $line;
-                            $count ++;
-                        }
-                        if ($count > 1) {
-                            $formatextra = '<div style="display:none" id="bubbletype">'.
-                                           &scantron_scantab().'</div>';
-                            $onclick = ' onclick="toggleScantab(this.form);"';
-                            $formatjs = <<"END";
+                    if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {  
+                        if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {
+                            my ($onclick,$formatextra,$singleline);
+                            my @lines = &Apache::lonnet::get_scantronformat_file();
+                            my $count = 0;
+                            foreach my $line (@lines) {
+                                next if ($line =~ /^#/);
+                                $singleline = $line;
+                                $count ++;
+                            }
+                            if ($count > 1) {
+                                $formatextra = '<div style="display:none" id="bubbletype">'.
+                                               '<span class="LC_nobreak">'.
+                                               &mt('Bubblesheet type:').'&nbsp;'.
+                                               &scantron_scantab().'</span></div>';
+                                $onclick = ' onclick="toggleScantab(this.form);"';
+                                $formatjs = <<"END";
 function toggleScantab(form) {
     var divid = 'bubbletype';
     if (document.getElementById(divid)) {
@@ -9125,7 +9127,7 @@ function toggleScantab(form) {
                     if (chosen == 'dat') {
                         document.getElementById(divid).style.display = 'none';
                     } else if (chosen == 'csv') {
-                        document.getElementById(divid).style.display = 'inline-block';
+                        document.getElementById(divid).style.display = 'block';
                     }
                 }
             }
@@ -9135,22 +9137,25 @@ function toggleScantab(form) {
 }
 
 END
-                        } elsif ($count == 1) {
-                            my $formatname = (split(/:/,$singleline,2))[0];
-                            $formatextra = '<input type="hidden" name="scantron_format" value="'.$formatname.'" />';
+                            } elsif ($count == 1) {
+                                my $formatname = (split(/:/,$singleline,2))[0];
+                                $formatextra = '<input type="hidden" name="scantron_format" value="'.$formatname.'" />';
+                            }
+                            $formattitle = &mt('File format');
+                            $formatoptions = '<label><input name="fileformat" type="radio" value="dat" checked="checked"'.$onclick.' />'.
+                                             &mt('Plain Text (no delimiters)').
+                                             '</label>'.('&nbsp;'x2).
+                                             '<label><input name="fileformat" type="radio" value="csv"'.$onclick.' />'.
+                                             &mt('Comma separated values').'</label>'.$formatextra;
                         }
-                        $formattitle = &mt('File format');
-                        $formatoptions = '<label><input name="fileformat" type="radio" value="dat" checked="checked"'.$onclick.' />'.
-                                         &mt('Plain Text (no delimiters)').
-                                         '</label>'.('&nbsp;'x2).
-                                         '<label><input name="fileformat" type="radio" value="csv"'.$onclick.' />'.
-                                         &mt('Comma separated values').'</label>'.$formatextra;
                     }
                 }
             } elsif (keys(%{$domconfig{'scantron'}{'config'}}) == 1) {
-                if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}})) {
-                    $formattitle = &mt('Format of bubblesheet data file:');
-                    $formatoptions = &scantron_scantab();
+                if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
+                    if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {
+                        $formattitle = &mt('Bubblesheet type');
+                        $formatoptions = &scantron_scantab();
+                    }
                 }
             }
         }
@@ -9159,7 +9164,7 @@ END
 }
 
 sub scantron_upload_scantron_data_save {
-    my($r,$symb)=@_;
+    my ($r,$symb) = @_;
     my $doanotherupload=
 	'<br /><form action="/adm/grades" method="post">'."\n".
 	'<input type="hidden" name="command" value="scantronupload" />'."\n".
@@ -9192,15 +9197,19 @@ sub scantron_upload_scantron_data_save {
                 if (@possibles > 1) {
                     if ($env{'form.fileformat'} eq 'csv') {
                         if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') {
-                            if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}}) > 1) {
-                                $is_csv = 1;
+                            if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
+                                if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}}) > 1) {
+                                    $is_csv = 1;
+                                }
                             }
                         }
                     }
                 } elsif (@possibles == 1) {
                     if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') {
-                        if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}}) > 1) {
-                            $is_csv = 1;
+                        if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
+                            if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}}) > 1) {
+                                $is_csv = 1;
+                            }
                         }
                     }
                 }
@@ -9356,7 +9365,7 @@ sub valid_file {
 }
 
 sub scantron_download_scantron_data {
-    my ($r,$symb)=@_;
+    my ($r,$symb) = @_;
     my $default_form_data=&defaultFormData($symb);
     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
@@ -9396,7 +9405,7 @@ sub checkscantron_results {
     my ($r,$symb) = @_;
     if (!$symb) {return '';}
     my $cid = $env{'request.course.id'};
-    my %lettdig = &letter_to_digits();
+    my %lettdig = &Apache::lonnet::letter_to_digits();
     my $numletts = scalar(keys(%lettdig));
     my $cnum = $env{'course.'.$cid.'.num'};
     my $cdom = $env{'course.'.$cid.'.domain'};
@@ -9727,23 +9736,6 @@ sub verify_scantron_grading {
     return ($counter,$record);
 }
 
-sub letter_to_digits {
-    my %lettdig = (
-                    A => 1,
-                    B => 2,
-                    C => 3,
-                    D => 4,
-                    E => 5,
-                    F => 6,
-                    G => 7,
-                    H => 8,
-                    I => 9,
-                    J => 0,
-                  );
-    return %lettdig;
-}
-
-
 #-------- end of section for handling grading scantron forms -------
 #
 #-------------------------------------------------------------------
@@ -9798,7 +9790,7 @@ sub grading_menu {
                     		icon => 'grade_students.png',
                     		linktitle => 'Grade current resource for a selection of students.'
                         }, 
-                        {       linktext => 'Grade ungraded submissions.',
+                        {       linktext => 'Grade ungraded submissions',
                                 url => $url1b,
                                 permission => 'F',
                                 icon => 'ungrade_sub.png',
@@ -9864,7 +9856,6 @@ sub grading_menu {
     return $Str;    
 }
 
-
 sub ungraded {
     my ($request)=@_;
     &submit_options($request);
@@ -9947,8 +9938,6 @@ sub submit_options {
 	      <input type="submit" value="'.&mt('Next').' &rarr;" />
             </div>
           </div>
-
-
   </form>';
     return $result;
 }
@@ -10216,12 +10205,12 @@ ENDUPFORM
 <input type="text" name="givenanswer" size="50" />
 <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />
 ENDGRADINGFORM
-         $result.='</td>'.&Apache::loncommon::end_data_table_row().
+    $result.='</td>'.&Apache::loncommon::end_data_table_row().
                      &Apache::loncommon::start_data_table_row().'<td>'.(<<ENDPERCFORM);
       <label>$pcorrect: <input type="text" name="pcorrect" size="4" value="$env{'form.pcorrect'}" onchange="sanitycheck()" /></label>
 <br /><label>$pincorrect: <input type="text" name="pincorrect" size="4" value="$env{'form.pincorrect'}" onchange="sanitycheck()" /></label>
 <br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />
-</form>'
+</form>
 ENDPERCFORM
     $result.='</td>'.
              &Apache::loncommon::end_data_table_row().
@@ -10230,7 +10219,7 @@ ENDPERCFORM
 }
 
 sub process_clicker_file {
-    my ($r,$symb)=@_;
+    my ($r,$symb) = @_;
     if (!$symb) {return '';}
 
     my %Saveable_Parameters=&clicker_grading_parameters();
@@ -10302,6 +10291,22 @@ sub process_clicker_file {
                         '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'),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.= '<p>'.
+                &Apache::lonhtmlcommon::confirm_success(
+                    &mt('File format is neither csv (iclicker 6) nor xml (iclicker 7)'),1).'</p>';
+            return $result;
+        }
+    } elsif (($env{'form.upfiletype'} ne 'interwrite') && ($env{'form.upfiletype'} ne 'turning')) {
+        $result .= '<p>'.
+            &Apache::lonhtmlcommon::confirm_success(
+                &mt('Invalid clicker type: choose one of: i>clicker, Interwrite PRS, or Turning Technologies.'),1).'</p>';
+        return $result;
+    }
 
 # Were able to get all the info needed, now analyze the file
 
@@ -10328,12 +10333,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.='<br />'.&mt('Found [_1] question(s)',$number).'<br />'.
@@ -10386,7 +10393,7 @@ ENDHEADER
                    "\n".&mt("Username").": <input type='text' name='uname".$id."' />&nbsp;".
                    "\n".&mt("Domain").": ".
                    &Apache::loncommon::select_dom_form($env{'course.'.$env{'request.course.id'}.'.domain'},'udom'.$id).'&nbsp;'.
-                   &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id,0,$id);
+                   &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id,'',$id);
           $unknown_count++;
        }
     }
@@ -10441,6 +10448,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;
@@ -10499,7 +10549,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;
@@ -10510,11 +10560,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().
-             '<th>'.&mt('Assigning grades based on clicker file').'</th>'.
-             &Apache::loncommon::end_data_table_header_row().
-             &Apache::loncommon::start_data_table_row().'<td>';
+    my $result = &Apache::loncommon::start_data_table().
+                 &Apache::loncommon::start_data_table_header_row().
+                 '<th>'.&mt('Assigning grades based on clicker file').'</th>'.
+                 &Apache::loncommon::end_data_table_header_row().
+                 &Apache::loncommon::start_data_table_row().'<td>';
 # Get correct result
 # FIXME: Possibly need delimiter other than ":"
     my @correct=();
@@ -10576,7 +10626,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 '*') {
@@ -10645,15 +10695,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));
     }
 }
 
@@ -10881,7 +10933,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();
@@ -11032,7 +11084,7 @@ Side Effects: None.
     $r           - Apache request object
     $i           - number of the current scanline
     $scan_record - hash ref as returned from &scantron_parse_scanline()
-    $scan_config - hash ref as returned from &get_scantron_config()
+    $scan_config - hash ref as returned from &Apache::lonnet::get_scantron_config()
     $line        - full contents of the current scanline
     $error       - error condition, valid values are
                    'incorrectCODE', 'duplicateCODE',