--- loncom/homework/grades.pm 2007/06/15 22:12:49 1.408 +++ loncom/homework/grades.pm 2007/06/22 22:50:30 1.417 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.408 2007/06/15 22:12:49 albertel Exp $ +# $Id: grades.pm,v 1.417 2007/06/22 22:50:30 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -617,7 +617,7 @@ sub verifyreceipt { if ($receipt eq &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb,$part)) { $contents.=' '."\n". ''.$$fullname{$_}.' '."\n". + '\');" target="_self">'.$$fullname{$_}.' '."\n". ' '.$uname.' '. ' '.$udom.' '; if ($receiptparts) { @@ -1792,10 +1792,10 @@ sub submission { # $request->print(<Keyword Options:  -List    +List    Paste Selection to List    -Highlight Attribute

+Highlight Attribute

KEYWORDS # # Load the other essays for similarity check @@ -1921,7 +1921,7 @@ KEYWORDS ' Collaborative submission by: '. ''. + '\');" target="_self">'. $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'
'; $request->print($submitby); next; @@ -1999,7 +1999,7 @@ KEYWORDS if ($env{'form.showgrading'} eq '' || (!&canmodify($usec))) { my $toGrade.='  '."\n" if (&canmodify($usec)); + .$counter.'\');" target="_self" />  '."\n" if (&canmodify($usec)); $toGrade.=''."\n"; if (($env{'form.command'} eq 'submission') || ($env{'form.command'} eq 'processGroup' && $counter == $total)) { @@ -2023,7 +2023,7 @@ KEYWORDS $result=''."\n". ''."\n"; $result.=' '. + ',\''.$msgfor.'\');" target="_self">'. &mt('Compose message to student').(scalar(@col_fullnames) >= 1 ? 's' : '').')'. ''."\n"; $endform.='  '."\n"; + $total.','.scalar(@partlist).');" target="_self" />  '."\n"; my $ntstu ='  '; + 'onClick="javascript:checksubmit(this.form,\'Next\');" target="_self" />  '; $endform.='(Next and Previous (student) do not save the scores.)'."\n" ; $endform.=""; @@ -3036,7 +3036,7 @@ sub viewgrades { $result.=''.''.''."\n". ''; $result.=''; + 'onClick="javascript:resetEntry('.$ctsparts.');" target="_self" />'; #table listing all the students in a section/class #header of table @@ -3087,7 +3087,7 @@ sub viewgrades { $result.=''; $result.=''."\n"; $result.=''."\n"; + 'onClick="javascript:submit();" target="_self" />'."\n"; if (scalar(%$fullname) eq 0) { my $colspan=3+scalar(@parts); $result='There are no students in section "'.$env{'form.section'}. @@ -3107,7 +3107,7 @@ sub viewstudentgrade { ''. "\n".$ctr.'  '. ''.$fullname.' '. + '\');" target="_self">'.$fullname.' '. '('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')'."\n"; $student=~s/:/_/; # colon doen't work in javascript for names foreach my $apart (@$parts) { @@ -6144,7 +6144,7 @@ sub gather_clicker_ids { my $clickers = (&Apache::lonnet::userenvironment($domain,$username,'clickers'))[1]; foreach my $id (split(/\,/,$clickers)) { - $id=~s/^0+//; + $id=~s/^[\#0]+//; if (exists($clicker_ids{$id})) { $clicker_ids{$id}.=','.$username.':'.$domain; } else { @@ -6160,13 +6160,13 @@ sub gather_adv_clicker_ids { my $cnum=$env{'course.'.$env{'request.course.id'}.'.num'}; my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'}; my %coursepersonnel=&Apache::lonnet::get_course_adv_roles($cdom.'/'.$cnum); - foreach my $element (sort keys %coursepersonnel) { + foreach my $element (sort(keys(%coursepersonnel))) { foreach my $person (split(/\,/,$coursepersonnel{$element})) { my ($puname,$pudom)=split(/\:/,$person); my $clickers = (&Apache::lonnet::userenvironment($pudom,$puname,'clickers'))[1]; foreach my $id (split(/\,/,$clickers)) { - $id=~s/^0+//; + $id=~s/^[\#0]+//; if (exists($clicker_ids{$id})) { $clicker_ids{$id}.=','.$puname.':'.$pudom; } else { @@ -6178,6 +6178,14 @@ sub gather_adv_clicker_ids { return %clicker_ids; } +sub clicker_grading_parameters { + return ('gradingmechanism' => 'scalar', + 'upfiletype' => 'scalar', + 'specificid' => 'scalar', + 'pcorrect' => 'scalar', + 'pincorrect' => 'scalar'); +} + sub process_clicker { my ($r)=@_; my ($symb)=&get_symb($r); @@ -6191,14 +6199,30 @@ sub process_clicker { $result.=' '.&mt('Specify a file containing the clicker information for this resource'). '.'."\n"; $result.=''."\n"; +# Attempt to restore parameters from last session, set defaults if not present + my %Saveable_Parameters=&clicker_grading_parameters(); + &Apache::loncommon::restore_course_settings('grades_clicker', + \%Saveable_Parameters); + if (!$env{'form.pcorrect'}) { $env{'form.pcorrect'}=100; } + if (!$env{'form.pincorrect'}) { $env{'form.pincorrect'}=100; } + if (!$env{'form.gradingmechanism'}) { $env{'form.gradingmechanism'}='attendance'; } + if (!$env{'form.upfiletype'}) { $env{'form.upfiletype'}='iclicker'; } + + my %checked; + foreach my $gradingmechanism ('attendance','personnel','specific') { + if ($env{'form.gradingmechanism'} eq $gradingmechanism) { + $checked{$gradingmechanism}="checked='checked'"; + } + } + my $upload=&mt("Upload File"); my $type=&mt("Type"); my $attendance=&mt("Award points just for participation"); my $personnel=&mt("Correctness determined from response by course personnel"); - my $specific=&mt("Correctness determined from response with clicker ID"); + my $specific=&mt("Correctness determined from response with clicker ID(s)"); my $pcorrect=&mt("Percentage points for correct solution"); my $pincorrect=&mt("Percentage points for incorrect solution"); - my $selectform=&Apache::loncommon::select_form('iclicker','upfiletype', + my $selectform=&Apache::loncommon::select_form($env{'form.upfiletype'},'upfiletype', ('iclicker' => 'i>clicker')); $result.=<
-
-
-
- - -
-
+
+
+
+ + +
+

ENDUPFORM @@ -6268,6 +6292,11 @@ sub process_clicker_file { my ($r)=@_; my ($symb)=&get_symb($r); if (!$symb) {return '';} + + my %Saveable_Parameters=&clicker_grading_parameters(); + &Apache::loncommon::store_course_settings('grades_clicker', + \%Saveable_Parameters); + my ($result) = &showResourceInfo($symb,$env{'form.probTitle'}); if (($env{'form.gradingmechanism'} eq 'specific') && ($env{'form.specificid'}!~/\w/)) { $result.=''.&mt('You need to specify a clicker ID for the correct answer').''; @@ -6279,28 +6308,31 @@ sub process_clicker_file { %correct_ids=&gather_adv_clicker_ids(); } if ($env{'form.gradingmechanism'} eq 'specific') { - my $correct_id=$env{'form.specificid'}; - $correct_id=~tr/a-z/A-Z/; - $correct_id=~s/\s//gs; - $correct_id=~s/^0+//; - $correct_ids{$correct_id}='specified'; + foreach my $correct_id (split(/[\s\,]/,$env{'form.specificid'})) {; + $correct_id=~tr/a-z/A-Z/; + $correct_id=~s/\s//gs; + $correct_id=~s/^[\#0]+//; + if ($correct_id) { + $correct_ids{$correct_id}='specified'; + } + } } if ($env{'form.gradingmechanism'} eq 'attendance') { $result.=&mt('Score based on attendance only'); } else { my $number=0; - $result.='

'.&mt('Correctness determined by the following IDs').'

'; + $result.='

'.&mt('Correctness determined by the following IDs').''; foreach my $id (sort(keys(%correct_ids))) { - $result.=''.$id.' - '; + $result.='
'.$id.' - '; if ($correct_ids{$id} eq 'specified') { $result.=&mt('specified'); } else { my ($uname,$udom)=split(/\:/,$correct_ids{$id}); $result.=&Apache::loncommon::plainname($uname,$udom); } - $result.='
'; $number++; } + $result.="

\n"; if ($number==0) { $result.=''.&mt('No IDs found to determine correct answer').''; return $result.&show_grading_menu_form($symb); @@ -6313,6 +6345,24 @@ sub process_clicker_file { ''.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').''); return $result.&show_grading_menu_form($symb); } + +# Were able to get all the info needed, now analyze the file + + $result.=&Apache::loncommon::studentbrowser_javascript(); + my $heading=&mt('Scanning clicker file'); + $result.=(<
+
+$heading
+
+ + + + + + + +ENDHEADER my %responses; my @questiontitles; my $errormsg=''; @@ -6320,10 +6370,55 @@ sub process_clicker_file { if ($env{'form.upfiletype'} eq 'iclicker') { ($errormsg,$number)=&iclicker_eval(\@questiontitles,\%responses); } - $result.='
'.&mt('Found [_1] questions',$number).'
'; - foreach my $id (keys %responses) { - $result.='
'.$id.' - '.$responses{$id}; + $result.='
'.&mt('Found [_1] question(s)',$number).'
'. + ''. + &mt('Awarding [_1] percent for correct and [_2] percent for incorrect responses', + $env{'form.pcorrect'},$env{'form.pincorrect'}). + '
'; +# Remember Question Titles +# FIXME: Possibly need delimiter other than ":" + for (my $i=0;$i<$number;$i++) { + $result.='').'" />'; + } + my $correct_count=0; + my $student_count=0; + my $unknown_count=0; +# Match answers with usernames +# FIXME: Possibly need delimiter other than ":" + foreach my $id (keys(%responses)) { + if ($correct_ids{$id}) { + $result.="\n".''; + $correct_count++; + } elsif ($clicker_ids{$id}) { + $result.="\n".''; + $student_count++; + } else { + $result.="\n
".&mt('Unregistered Clicker')." ".$id."
"; + $result.="\n".''. + "\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); + $unknown_count++; + } + } + $result.='
'. + &mt('Found [_1] registered and [_2] unregistered clickers.',$student_count,$unknown_count); + if ($env{'form.gradingmechanism'} ne 'attendance') { + if ($correct_count==0) { + $errormsg.="Found no correct answers answers for grading!"; + } elsif ($correct_count>1) { + $result.='
'.&mt("Found [_1] entries for grading!",$correct_count).''; + } + } + if ($errormsg) { + $result.='
'.&mt($errormsg).''; + } else { + $result.='
'; } + $result.='
'."\n". + '


'."\n"; return $result.&show_grading_menu_form($symb); } @@ -6332,14 +6427,8 @@ sub iclicker_eval { my $number=0; my $errormsg=''; foreach my $line (split(/[\n\r]/,$env{'form.upfile'})) { - chomp($line); - foreach my $quoted ($line=~/\,\s*\"([^\"]*)\"\s*\,/g) { - my $replace=$quoted; - $replace=~s/\,//g; - &Apache::lonnet::logthis($quoted.' - '.$replace.'
'); - $line=~s/\,\s*\"\Q$quoted\E\"\s*\,/,$replace,/gs; - } - my @entries=split(/\,/,$line); + my %components=&Apache::loncommon::record_sep($line); + my @entries=map {$components{$_}} (sort(keys(%components))); if ($entries[0] eq 'Question') { for (my $i=3;$i<$#entries;$i+=6) { $$questiontitles[$number]=$entries[$i]; @@ -6360,6 +6449,99 @@ sub iclicker_eval { return ($errormsg,$number); } +sub assign_clicker_grades { + my ($r)=@_; + my ($symb)=&get_symb($r); + if (!$symb) {return '';} +# See which part we are saving to + my ($partlist,$handgrade,$responseType) = &response_type($symb); +# FIXME: This should probably look for the first handgradeable part + my $part=$$partlist[0]; +# Start screen output + my ($result) = &showResourceInfo($symb,$env{'form.probTitle'}); + + my $heading=&mt('Assigning grades based on clicker file'); + $result.=(<
+
+$heading
+ENDHEADER +# Get correct result +# FIXME: Possibly need delimiter other than ":" + my @correct=(); + my $gradingmechanism=$env{'form.gradingmechanism'}; + my $number=$env{'form.number'}; + if ($gradingmechanism ne 'attendance') { + foreach my $key (keys(%env)) { + if ($key=~/^form\.correct\:/) { + my @input=split(/\,/,$env{$key}); + for (my $i=0;$i<=$#input;$i++) { + if (($correct[$i]) && ($input[$i]) && + ($correct[$i] ne $input[$i])) { + $result.='
'. + &mt('More than one correct result given for question "[_1]": [_2] versus [_3].', + $env{'form.question:'.$i},$correct[$i],$input[$i]).''; + } elsif ($input[$i]) { + $correct[$i]=$input[$i]; + } + } + } + } + for (my $i=0;$i<$number;$i++) { + if (!$correct[$i]) { + $result.='
'. + &mt('No correct result given for question "[_1]"!', + $env{'form.question:'.$i}).''; + } + } + $result.='
'.&mt("Correct answer: [_1]",join(', ',map { ($_?$_:'-') } @correct)); + } +# Start grading + my $pcorrect=$env{'form.pcorrect'}; + my $pincorrect=$env{'form.pincorrect'}; + my $storecount=0; + foreach my $key (keys(%env)) { + if ($key=~/^form\.student\:(.*)$/) { + my $user=$1; + my @answer=split(/\,/,$env{$key}); + my $sum=0; + for (my $i=0;$i<$number;$i++) { + if ($answer[$i]) { + if ($gradingmechanism eq 'attendance') { + $sum+=$pcorrect; + } else { + if ($answer[$i] eq $correct[$i]) { + $sum+=$pcorrect; + } else { + $sum+=$pincorrect; + } + } + } + } + my $ave=$sum/(100*$number); +# Store + my ($username,$domain)=split(/\:/,$user); + my %grades=(); + $grades{"resource.$part.solved"}='correct_by_override'; + $grades{"resource.$part.awarded"}=$ave; + $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}"; + my $returncode=&Apache::lonnet::cstore(\%grades,$symb, + $env{'request.course.id'}, + $domain,$username); + if ($returncode ne 'ok') { + $result.="
Failed to save student $username:$domain. Message when trying to save was ($returncode)"; + } else { + $storecount++; + } + } + } +# We are done + $result.='
'.&mt('Successfully stored grades for [_1] student(s).',$storecount). + '
'."\n". + '


'."\n"; + return $result.&show_grading_menu_form($symb); +} + sub handler { my $request=$_[0]; @@ -6431,6 +6613,8 @@ sub handler { $request->print(&process_clicker($request)); } elsif ($command eq 'processclickerfile' && $perm{'mgr'}) { $request->print(&process_clicker_file($request)); + } elsif ($command eq 'assignclickergrades' && $perm{'mgr'}) { + $request->print(&assign_clicker_grades($request)); } elsif ($command eq 'csvform' && $perm{'mgr'}) { $request->print(&upcsvScores_form($request)); } elsif ($command eq 'csvupload' && $perm{'mgr'}) {