--- loncom/homework/grades.pm 2002/08/02 21:10:03 1.44
+++ loncom/homework/grades.pm 2003/09/18 17:20:05 1.138
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.44 2002/08/02 21:10:03 ng Exp $
+# $Id: grades.pm,v 1.138 2003/09/18 17:20:05 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -31,6 +31,9 @@
# 8/20 Gerd Kortemeyer
# Year 2002
# June-August H.K. Ng
+# Year 2003
+# February, March H.K. Ng
+# July, H. K. Ng
#
package Apache::grades;
@@ -39,11 +42,18 @@ use Apache::style;
use Apache::lonxml;
use Apache::lonnet;
use Apache::loncommon;
+use Apache::lonhtmlcommon;
+use Apache::lonnavmaps;
use Apache::lonhomework;
+use Apache::loncoursedata;
use Apache::lonmsg qw(:user_normal_msg);
use Apache::Constants qw(:common);
+use String::Similarity;
-# ----- These first few routines are general use routines.-----
+my %oldessays=();
+my %perm=();
+
+# ----- These first few routines are general use routines.----
#
# --- Retrieve the parts that matches stores_\d+ from the metadata file.---
sub getpartlist {
@@ -51,7 +61,7 @@ sub getpartlist {
my @parts =();
my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
foreach my $key (@metakeys) {
- if ( $key =~ m/stores_([0-9]+)_.*/) {
+ if ( $key =~ m/stores_(\w+)_.*/) {
push(@parts,$key);
}
}
@@ -72,29 +82,47 @@ sub get_symb_and_url {
sub get_fullname {
my ($uname,$udom) = @_;
my %name=&Apache::lonnet::get('environment', ['lastname','generation',
- 'firstname','middlename'],$udom,$uname);
+ 'firstname','middlename'],
+ $udom,$uname);
my $fullname;
my ($tmp) = keys(%name);
if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
- $fullname=$name{'lastname'}.$name{'generation'};
- if ($fullname =~ /[^\s]+/) { $fullname.=', '; }
- $fullname.=$name{'firstname'}.' '.$name{'middlename'};
+ $fullname = &Apache::loncoursedata::ProcessFullName
+ (@name{qw/lastname generation firstname middlename/});
+ } else {
+ &Apache::lonnet::logthis('grades.pm: no name data for '.$uname.
+ '@'.$udom.':'.$tmp);
}
return $fullname;
}
+#--- Format fullname, username:domain if different for display
+#--- Use anywhere where the student names are listed
+sub nameUserString {
+ my ($type,$fullname,$uname,$udom) = @_;
+ if ($type eq 'header') {
+ return ' Fullname (Username) ';
+ } else {
+ return ' '.$fullname.' ('.$uname.
+ ($ENV{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')';
+ }
+}
+
#--- Get the partlist and the response type for a given problem. ---
#--- Indicate if a response type is coded handgraded or not. ---
sub response_type {
- my ($url) = shift;
+ my ($url,$symb) = shift;
+ $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url))) if ($symb eq '');
my $allkeys = &Apache::lonnet::metadata($url,'keys');
my %seen = ();
my (@partlist,%handgrade);
foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) {
- if (/^\w+response_\d+.*/) {
+ if (/^\w+response_\w+.*/) {
my ($responsetype,$part) = split(/_/,$_,2);
my ($partid,$respid) = split(/_/,$part);
- $handgrade{$part} = $responsetype.':'.($allkeys =~ /parameter_$part\_handgrade/ ? 'yes' : 'no');
+ $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!!
+ my ($value) = &Apache::lonnet::EXT('resource.'.$part.'.handgrade',$symb);
+ $handgrade{$part} = $responsetype.':'.($value eq 'yes' ? 'yes' : 'no');
next if ($seen{$partid} > 0);
$seen{$partid}++;
push @partlist,$partid;
@@ -103,110 +131,178 @@ sub response_type {
return \@partlist,\%handgrade;
}
-#--- Prints a message on screen if a user did something wrong
-#--- Operator error ---
-sub userError {
- my ($request, $reason, $step) = @_;
- $request->print('
LON-CAPA User Error
'."\n");
- $request->print('Reason: '.$reason.'
'."\n");
- $request->print('Step: '.($step ne '' ? $step : 'Use your browser back button to correct')
- .'
'."\n");
- return '';
+#--- Show resource title
+#--- and parts and response type
+sub showResourceInfo {
+ my ($url,$probTitle) = @_;
+ my $result ='
'.
+ '
Current Resource: '.$probTitle.'
'."\n";
+ my ($partlist,$handgrade) = &response_type($url);
+ my %resptype = ();
+ my $hdgrade='no';
+ for (sort keys(%$handgrade)) {
+ my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
+ my $partID = (split(/_/))[0];
+ $resptype{$partID} = $responsetype;
+ $hdgrade = $handgrade if ($handgrade eq 'yes');
+ $result.='
Part '.$partID.'
'.
+ '
Type: '.$responsetype.'
';
+# '
Handgrade: '.$handgrade.'
';
+ }
+ $result.='
'."\n";
+ return $result,\%resptype,$hdgrade,$partlist,$handgrade;
+}
+
+#--- Clean response type for display
+#--- Currently filters option response type only.
+sub cleanRecord {
+ my ($answer,$response,$symb) = @_;
+ if ($response eq 'option') {
+ my (@IDs,@ans);
+ foreach (split(/\&/,&Apache::lonnet::unescape($answer))) {
+ my ($optionID,$ans) = split(/=/);
+ push @IDs,$optionID.'';
+ push @ans,$ans;
+ }
+ my $grayFont = '';
+ return '
'.
+ '
Answer
'.
+ (join '
',@ans).'
'.
+ '
'.$grayFont.'Option ID
'.$grayFont.
+ (join '
'.$grayFont,@IDs).'
'.
+ '
';
+ }
+ if ($response eq 'essay') {
+ if (! exists ($ENV{'form.'.$symb})) {
+ my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade',
+ $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
+ $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
+
+ my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};
+ $ENV{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
+ $ENV{'form.kwclr'} = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
+ $ENV{'form.kwsize'} = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
+ $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.
+ }
+ return '
'.&keywords_highlight($answer).'
';
+ }
+ return $answer;
+}
+
+#-- A couple of common js functions
+sub commonJSfunctions {
+ my $request = shift;
+ $request->print(<
+ function radioSelection(radioButton) {
+ var selection=null;
+ if (radioButton.length > 1) {
+ for (var i=0; i 1) {
+ for (var i=0; i
+COMMONJSFUNCTIONS
}
#--- Dumps the class list with usernames,list of sections,
#--- section, ids and fullnames for each user.
sub getclasslist {
- my ($getsec,$hideexpired) = @_;
- my $now = time;
- my %classlist=&Apache::lonnet::dump('classlist',
- $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
- $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
- # codes to check for fields in the classlist
- # should contain end:start:id:section:fullname
- for (keys %classlist) {
- my (@fields) = split(/:/,$classlist{$_});
- %classlist = &reformat_classlist(\%classlist) if (scalar(@fields) <= 2);
- last;
- }
-
- my (@holdsec,@sections,%allids,%stusec,%fullname);
- foreach (keys(%classlist)) {
- my ($end,$start,$id,$section,$fullname)=split(/:/,$classlist{$_});
- # still a student?
- if (($hideexpired) && ($end) && ($end < $now)) {
- next;
+ my ($getsec,$filterlist) = @_;
+ $getsec = $getsec eq '' ? 'all' : $getsec;
+ my $classlist=&Apache::loncoursedata::get_classlist();
+ # Bail out if we were unable to get the classlist
+ return if (! defined($classlist));
+ #
+ my %sections;
+ my %fullnames;
+ foreach (keys(%$classlist)) {
+ # the following undefs are for 'domain', and 'username' respectively.
+ my (undef,undef,$end,$start,$id,$section,$fullname,$status)=
+ @{$classlist->{$_}};
+ # filter students according to status selected
+ if ($filterlist && $ENV{'form.Status'} ne 'Any') {
+ if ($ENV{'form.Status'} ne $status) {
+ delete ($classlist->{$_});
+ next;
+ }
}
$section = ($section ne '' ? $section : 'no');
- push @holdsec,$section;
- if ($getsec eq 'all' || $getsec eq $section) {
- push (@{ $classlist{$getsec} }, $_);
- $allids{$_} =$id;
- $stusec{$_} =$section;
- $fullname{$_}=$fullname;
+ if (&canview($section)) {
+ if ($getsec eq 'all' || $getsec eq $section) {
+ $sections{$section}++;
+ $fullnames{$_}=$fullname;
+ } else {
+ delete($classlist->{$_});
+ }
+ } else {
+ delete($classlist->{$_});
}
}
my %seen = ();
- foreach my $item (@holdsec) {
- push (@sections, $item) unless $seen{$item}++;
- }
- return (\%classlist,\@sections,\%allids,\%stusec,\%fullname);
+ my @sections = sort(keys(%sections));
+ return ($classlist,\@sections,\%fullnames);
}
-# add id, section and fullname to the classlist.db
-# done to maintain backward compatibility with older versions
-sub reformat_classlist {
- my ($classlist) = shift;
- foreach (sort keys(%$classlist)) {
- my ($unam,$udom) = split(/:/);
- my $section = &Apache::lonnet::usection($udom,$unam,$ENV{'request.course.id'});
- my $fullname = &get_fullname ($unam,$udom);
- my %userid = &Apache::lonnet::idrget($udom,($unam));
- $$classlist{$_} = $$classlist{$_}.':'.$userid{$unam}.':'.$section.':'.$fullname;
- }
- my $putresult = &Apache::lonnet::put
- ('classlist',\%$classlist,
- $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
- $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
-
- return %$classlist;
-}
-
-#find user domain
-sub finduser {
- my ($name) = @_;
- my $domain = '';
- if ( $Apache::grades::viewgrades eq 'F' ) {
- my %classlist=&Apache::lonnet::dump('classlist',
- $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
- $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
- my (@fields) = grep /^$name:/, keys %classlist;
- ($name, $domain) = split(/:/,$fields[0]);
- return ($name,$domain);
- } else {
- return ($ENV{'user.name'},$ENV{'user.domain'});
+sub canmodify {
+ my ($sec)=@_;
+ if ($perm{'mgr'}) {
+ if (!defined($perm{'mgr_section'})) {
+ # can modify whole class
+ return 1;
+ } else {
+ if ($sec eq $perm{'mgr_section'}) {
+ #can modify the requested section
+ return 1;
+ } else {
+ # can't modify the request section
+ return 0;
+ }
+ }
}
+ #can't modify
+ return 0;
}
-#--- Prompts a user to enter a username.
-sub moreinfo {
- my ($request,$reason) = @_;
- $request->print("Unable to process request: $reason");
- if ( $Apache::grades::viewgrades eq 'F' ) {
- $request->print('');
+sub canview {
+ my ($sec)=@_;
+ if ($perm{'vgr'}) {
+ if (!defined($perm{'vgr_section'})) {
+ # can modify whole class
+ return 1;
+ } else {
+ if ($sec eq $perm{'vgr_section'}) {
+ #can modify the requested section
+ return 1;
+ } else {
+ # can't modify the request section
+ return 0;
+ }
+ }
}
- return '';
+ #can't modify
+ return 0;
}
#--- Retrieve the grade status of a student for all the parts
@@ -215,7 +311,7 @@ sub student_gradeStatus {
my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
my %partstatus = ();
foreach (@$partlist) {
- my ($status,$foo) = split(/_/,$record{"resource.$_.solved"},2);
+ my ($status,undef) = split(/_/,$record{"resource.$_.solved"},2);
$status = 'nothing' if ($status eq '');
$partstatus{$_} = $status;
my $subkey = "resource.$_.submitted_by";
@@ -224,11 +320,80 @@ sub student_gradeStatus {
return %partstatus;
}
+# hidden form and javascript that calls the form
+# Use by verifyscript and viewgrades
+# Shows a student's view of problem and submission
+sub jscriptNform {
+ my ($url,$symb) = @_;
+ my $jscript=''."\n";
+ $jscript.= ''."\n";
+ return $jscript;
+}
#------------------ End of general use routines --------------------
+
+#
+# Find most similar essay
+#
+
+sub most_similar {
+ my ($uname,$udom,$uessay)=@_;
+
+# ignore spaces and punctuation
+
+ $uessay=~s/\W+/ /gs;
+
+# these will be returned. Do not care if not at least 50 percent similar
+ my $limit=0.6;
+ my $sname='';
+ my $sdom='';
+ my $scrsid='';
+ my $sessay='';
+# go through all essays ...
+ foreach my $tkey (keys %oldessays) {
+ my ($tname,$tdom,$tcrsid)=split(/\./,$tkey);
+# ... except the same student
+ if (($tname ne $uname) || ($tdom ne $udom)) {
+ my $tessay=$oldessays{$tkey};
+ $tessay=~s/\W+/ /gs;
+# String similarity gives up if not even limit
+ my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit);
+# Found one
+ if ($tsimilar>$limit) {
+ $limit=$tsimilar;
+ $sname=$tname;
+ $sdom=$tdom;
+ $scrsid=$tcrsid;
+ $sessay=$oldessays{$tkey};
+ }
+ }
+ }
+ if ($limit>0.6) {
+ return ($sname,$sdom,$scrsid,$sessay,$limit);
+ } else {
+ return ('','','','',0);
+ }
+}
+
#-------------------------------------------------------------------
#------------------------------------ Receipt Verification Routines
+#
#--- Check whether a receipt number is valid.---
sub verifyreceipt {
my $request = shift;
@@ -243,29 +408,14 @@ sub verifyreceipt {
$symb = &Apache::lonnet::symbread($url);
}
- my $jscript=''."\n";
- $jscript.= ''."\n";
-
- my $title.='
'."\n";
my ($string,$contents,$matches) = ('','',0);
- my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist('all','0');
-
- foreach (sort {$$fullname{$a} cmp $$fullname{$b} } keys %$fullname) {
+ my (undef,undef,$fullname) = &getclasslist('all','0');
+
+ foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
my ($uname,$udom)=split(/\:/);
if ($receipt eq
&Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) {
@@ -281,7 +431,7 @@ sub verifyreceipt {
if ($matches == 0) {
$string = $title.'No match found for the above receipt.';
} else {
- $string = $jscript.$title.
+ $string = &jscriptNform($url,$symb).$title.
'The above receipt matches the following student'.
($matches <= 1 ? '.' : 's.')."\n".
'
'."\n".
@@ -292,7 +442,7 @@ sub verifyreceipt {
$contents.
'
'."\n";
}
- return $string.&show_grading_menu_form ($symb,$url);
+ return $string.&show_grading_menu_form($symb,$url);
}
#--- This is called by a number of programs.
@@ -301,96 +451,184 @@ sub verifyreceipt {
# on the problem page.
sub listStudents {
my ($request) = shift;
+
+ my ($symb,$url) = &get_symb_and_url($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 $submitonly= $ENV{'form.submitonly'} eq '' ? 'all' : $ENV{'form.submitonly'};
- my $result='
'.
- 'View Submissions for a Student or a Group of Students
';
- $result.='
';
- $result.='
'.
- 'Resource: '.$ENV{'form.url'}.'
';
- my ($partlist,$handgrade) = &response_type($ENV{'form.url'});
- for (sort keys(%$handgrade)) {
- my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
- $ENV{'form.handgrade'} = 'yes' if ($handgrade eq 'yes');
- $result.='
'."\n".
+ ' Resource: '.$ENV{'form.probTitle'}.''."\n");
+
+ if ($ENV{'form.handgrade'} eq 'no') {
+ my $checkMark='
Note: Part(s) graded correct by the computer is marked with a '.
+ $checkIcon.' symbol.'."\n";
+ $request->print($checkMark);
+ }
# option to display problem, only once else it cause problems
# with the form later since the problem has a form.
- if ($ENV{'form.vProb'} eq 'yes') {
- my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
- $ENV{'request.course.id'});
- my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,
- $ENV{'request.course.id'});
- my $result.='
';
- $result.='
';
- $result.=' View of the problem for '.$ENV{'form.fullname'}.
- '
';
- $request->print($result);
+ if ($ENV{'form.vProb'} eq 'yes' or !$ENV{'form.vProb'}) {
+ $request->print(&show_problem($request,$symb,$uname,$udom,0,1));
}
# kwclr is the only variable that is guaranteed to be non blank
# if this subroutine has been called once.
my %keyhash = ();
- if ($ENV{'form.kwclr'} eq '') {
+ if ($ENV{'form.kwclr'} eq '' && $ENV{'form.handgrade'} eq 'yes') {
%keyhash = &Apache::lonnet::dump('nohist_handgrade',
$ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
$ENV{'course.'.$ENV{'request.course.id'}.'.num'});
@@ -800,14 +1283,20 @@ sub submission {
$ENV{'form.kwsize'} = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
$ENV{'form.kwstyle'} = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
$ENV{'form.msgsub'} = $keyhash{$symb.'_subject'} ne '' ?
- $keyhash{$symb.'_subject'} : &Apache::lonnet::metadata($url,'title');
+ $keyhash{$symb.'_subject'} : $ENV{'form.probTitle'};
$ENV{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';
-
}
+ my $overRideScore = $ENV{'form.overRideScore'} eq '' ? 'no' : $ENV{'form.overRideScore'};
$request->print('
'."\n");
+ $result=''."\n";
+ $result.=''."\n" if ($counter == 0);
+ my $ctr = 0;
+ while ($ctr < scalar(@partlist)) {
+ $result.=''."\n";
+ $ctr++;
+ }
+ $request->print($result.''."\n");
# print end of form
if ($counter == $total) {
- my $endform.='
';
+ my $endform='
'."\n";
+ $endform.=' '."\n";
my $ntstu =''."\n";
my $nsel = ($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1');
$ntstu =~ s/
';
+ $endform.=$ntstu.'student(s) ';
+ $endform.=' '."\n".
+ ' ';
+ $endform.='(Next and Previous (student) do not save the scores.)'."\n" ;
+ $endform.='
';
+ $endform.=&show_grading_menu_form($symb,$url);
$request->print($endform);
}
return '';
@@ -1049,31 +1599,28 @@ KEYWORDS
#--- Retrieve the last submission for all the parts
sub get_last_submission {
- my ($symb,$username,$domain,$course)=@_;
- if ($symb) {
- my (@string,$timestamp);
- my (%returnhash)=&Apache::lonnet::restore($symb,$course,$domain,$username);
- if ($returnhash{'version'}) {
- my %lasthash=();
- my ($version);
- for ($version=1;$version<=$returnhash{'version'};$version++) {
- foreach (sort(split(/\:/,$returnhash{$version.':keys'}))) {
- $lasthash{$_}=$returnhash{$version.':'.$_};
- }
- }
- foreach ((keys %lasthash)) {
- if ($_ =~ /\.submission$/) {
- my ($partid,$foo) = split(/submission$/,$_);
- my $draft = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ?
- 'Draft Copy ' : '';
- push @string, (join(':',$_,$draft.$lasthash{$_}));
- }
- if ($_ =~ /timestamp/) {$timestamp = scalar(localtime($lasthash{$_}))};
+ my ($returnhash)=@_;
+ my (@string,$timestamp);
+ if ($$returnhash{'version'}) {
+ my %lasthash=();
+ my ($version);
+ for ($version=1;$version<=$$returnhash{'version'};$version++) {
+ foreach (sort(split(/\:/,$$returnhash{$version.':keys'}))) {
+ $lasthash{$_}=$$returnhash{$version.':'.$_};
+ $timestamp = scalar(localtime($$returnhash{$version.':timestamp'}));
+ }
+ }
+ foreach ((keys %lasthash)) {
+ if ($_ =~ /\.submission$/) {
+ my ($partid,$foo) = split(/submission$/,$_);
+ my $draft = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ?
+ 'Draft Copy ' : '';
+ push @string, (join(':',$_,$draft.$lasthash{$_}));
}
}
- @string = $string[0] eq '' ? 'Nothing submitted - no attempts.' : @string;
- return \@string,\$timestamp;
}
+ @string = $string[0] eq '' ? 'Nothing submitted - no attempts.' : @string;
+ return \@string,\$timestamp;
}
#--- High light keywords, with style choosen by user.
@@ -1084,7 +1631,7 @@ sub keywords_highlight {
(my $styleoff = $styleon) =~ s/\\<\//;
my @keylist = split(/[,\s+]/,$ENV{'form.keywords'});
foreach (@keylist) {
- $string =~ s/\b$_(\b|\.)/\$styleon$_$styleoff\<\/font\>/gi;
+ $string =~ s/\b\Q$_\E(\b|\.)/$styleon$_$styleoff<\/font>/gi;
}
return $string;
}
@@ -1097,35 +1644,50 @@ sub processHandGrade {
my $button = $ENV{'form.gradeOpt'};
my $ngrade = $ENV{'form.NCT'};
my $ntstu = $ENV{'form.NTSTU'};
-
if ($button eq 'Save & Next') {
my $ctr = 0;
while ($ctr < $ngrade) {
my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr});
- my ($errorflg) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr);
- return '' if ($errorflg eq 'error');
-
+ my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr);
+ if ($errorflag eq 'no_score') {
+ $ctr++;
+ next;
+ }
+ if ($errorflag eq 'not_allowed') {
+ $request->print("Not allowed to modify grades for $uname:$udom");
+ $ctr++;
+ next;
+ }
my $includemsg = $ENV{'form.includemsg'.$ctr};
my ($subject,$message,$msgstatus) = ('','','');
- if ($includemsg =~ /savemsg|new$ctr/) {
+ if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) {
$subject = $ENV{'form.msgsub'} if ($includemsg =~ /^msgsub/);
my (@msgnum) = split(/,/,$includemsg);
foreach (@msgnum) {
$message.=$ENV{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne '');
}
- $message =~ s/\s+/ /g;
+ $message =&Apache::lonfeedback::clear_out_html($message);
+ $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;
+ $message.=" for $ENV{'form.probTitle'}";
$msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom,
$ENV{'form.msgsub'},$message);
}
if ($ENV{'form.collaborator'.$ctr}) {
my (@collaborators) = split(/:/,$ENV{'form.collaborator'.$ctr});
foreach (@collaborators) {
- &saveHandGrade($request,$url,$symb,$_,$udom,$ctr,
- $ENV{'form.unamedom'.$ctr});
- if ($message ne '') {
- $msgstatus = &Apache::lonmsg::user_normal_msg ($_,$udom,
- $ENV{'form.msgsub'},
- $message);
+ my ($errorflag,$pts,$wgt) =
+ &saveHandGrade($request,$url,$symb,$_,$udom,$ctr,$ENV{'form.unamedom'.$ctr});
+ if ($errorflag eq 'not_allowed') {
+ $request->print("Not allowed to modify grades for $_:$udom");
+ next;
+ } else {
+ if ($message ne '') {
+ $msgstatus = &Apache::lonmsg::user_normal_msg ($_,$udom,
+ $ENV{'form.msgsub'},
+ $message);
+ }
}
}
}
@@ -1133,72 +1695,90 @@ sub processHandGrade {
}
}
- # Keywords sorted in alphabatical order
- my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};
- my %keyhash = ();
- $ENV{'form.keywords'} =~ s/,\s{0,}|\s+/ /g;
- $ENV{'form.keywords'} =~ s/^\s+|\s+$//;
- my (@keywords) = sort(split(/\s+/,$ENV{'form.keywords'}));
- $ENV{'form.keywords'} = join(' ',@keywords);
- $keyhash{$symb.'_keywords'} = $ENV{'form.keywords'};
- $keyhash{$symb.'_subject'} = $ENV{'form.msgsub'};
- $keyhash{$loginuser.'_kwclr'} = $ENV{'form.kwclr'};
- $keyhash{$loginuser.'_kwsize'} = $ENV{'form.kwsize'};
- $keyhash{$loginuser.'_kwstyle'} = $ENV{'form.kwstyle'};
-
- # message center - Order of message gets changed. Blank line is eliminated.
- # New messages are saved in ENV for the next student.
- # All messages are saved in nohist_handgrade.db
- my ($ctr,$idx) = (1,1);
- while ($ctr <= $ENV{'form.savemsgN'}) {
- if ($ENV{'form.savemsg'.$ctr} ne '') {
- $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.savemsg'.$ctr};
- $idx++;
+ if ($ENV{'form.handgrade'} eq 'yes') {
+ # Keywords sorted in alphabatical order
+ my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};
+ my %keyhash = ();
+ $ENV{'form.keywords'} =~ s/,\s{0,}|\s+/ /g;
+ $ENV{'form.keywords'} =~ s/^\s+|\s+$//;
+ my (@keywords) = sort(split(/\s+/,$ENV{'form.keywords'}));
+ $ENV{'form.keywords'} = join(' ',@keywords);
+ $keyhash{$symb.'_keywords'} = $ENV{'form.keywords'};
+ $keyhash{$symb.'_subject'} = $ENV{'form.msgsub'};
+ $keyhash{$loginuser.'_kwclr'} = $ENV{'form.kwclr'};
+ $keyhash{$loginuser.'_kwsize'} = $ENV{'form.kwsize'};
+ $keyhash{$loginuser.'_kwstyle'} = $ENV{'form.kwstyle'};
+
+ # message center - Order of message gets changed. Blank line is eliminated.
+ # New messages are saved in ENV for the next student.
+ # All messages are saved in nohist_handgrade.db
+ my ($ctr,$idx) = (1,1);
+ while ($ctr <= $ENV{'form.savemsgN'}) {
+ if ($ENV{'form.savemsg'.$ctr} ne '') {
+ $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.savemsg'.$ctr};
+ $idx++;
+ }
+ $ctr++;
}
- $ctr++;
- }
- $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++;
+ $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++;
+ }
+ $ctr++;
}
- $ctr++;
+ $ENV{'form.savemsgN'} = --$idx;
+ $keyhash{$symb.'_savemsgN'} = $ENV{'form.savemsgN'};
+ my $putresult = &Apache::lonnet::put
+ ('nohist_handgrade',\%keyhash,
+ $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
+ $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
}
- $ENV{'form.savemsgN'} = --$idx;
- $keyhash{$symb.'_savemsgN'} = $ENV{'form.savemsgN'};
- my $putresult = &Apache::lonnet::put
- ('nohist_handgrade',\%keyhash,
- $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
- $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
-
# Called by Save & Refresh from Highlight Attribute Window
+ my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'1');
if ($ENV{'form.refresh'} eq 'on') {
- my $ctr = 0;
- $ENV{'form.NTSTU'}=$ngrade;
+ my ($ctr,$total) = (0,0);
while ($ctr < $ngrade) {
- ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$ENV{'form.unamedom'.$ctr});
- &submission($request,$ctr,$ngrade-1);
+ $total++ if $ENV{'form.unamedom'.$ctr} ne '';
$ctr++;
}
+ $ENV{'form.NTSTU'}=$ngrade;
+ $ctr = 0;
+ while ($ctr < $total) {
+ my $processUser = $ENV{'form.unamedom'.$ctr};
+ ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$processUser);
+ $ENV{'form.fullname'} = $$fullname{$processUser};
+ &submission($request,$ctr,$total-1);
+ $ctr++;
+ }
+ return '';
+ }
+
+# Go directly to grade student - from submission or link from chart page
+ if ($button eq 'Grade Student') {
+ (undef,undef,$ENV{'form.handgrade'},undef,undef) = &showResourceInfo($url);
+ my $processUser = $ENV{'form.unamedom'.$ENV{'form.studentNo'}};
+ ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$processUser);
+ $ENV{'form.fullname'} = $$fullname{$processUser};
+ &submission($request,0,0);
return '';
}
# Get the next/previous one or group of students
my $firststu = $ENV{'form.unamedom0'};
my $laststu = $ENV{'form.unamedom'.($ngrade-1)};
- $ctr = 2;
+ my $ctr = 2;
while ($laststu eq '') {
$laststu = $ENV{'form.unamedom'.($ngrade-$ctr)};
$ctr++;
$laststu = $firststu if ($ctr > $ngrade);
}
- my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0');
my (@parsedlist,@nextlist);
my ($nextflg) = 0;
- foreach (sort {$$fullname{$a} cmp $$fullname{$b} } keys %$fullname) {
+ foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
if ($nextflg == 1 && $button =~ /Next$/) {
push @parsedlist,$_;
}
@@ -1209,21 +1789,19 @@ sub processHandGrade {
}
}
$ctr = 0;
- my ($partlist,$handgrade) = &response_type($ENV{'form.url'});
@parsedlist = reverse @parsedlist if ($button eq 'Previous');
foreach my $student (@parsedlist) {
my ($uname,$udom) = split(/:/,$student);
if ($ENV{'form.submitonly'} eq 'yes') {
- my (%status) = &student_gradeStatus($ENV{'form.url'},$symb,$udom,$uname,$partlist) ;
+ my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
my $statusflg = '';
- foreach (keys(%status)) {
- $statusflg = 1 if ($status{$_} ne 'nothing');
- my ($foo,$partid,$foo1) = split(/\./);
- $statusflg = '' if ($status{'resource.'.$partid.'.submitted_by'} ne '');
+ foreach (split(/:/,$ENV{'form.gradePartRespid'})){
+ $statusflg = 1 if (exists ($record{'resource.'.$_.'.submission'}));
}
next if ($statusflg eq '');
}
push @nextlist,$student if ($ctr < $ntstu);
+ last if ($ctr == $ntstu);
$ctr++;
}
@@ -1235,8 +1813,6 @@ sub processHandGrade {
$ENV{'form.student'} = $uname;
$ENV{'form.userdom'} = $udom;
$ENV{'form.fullname'} = $$fullname{$_};
-# $ENV{'form.'.$_.':submitted_by'} = $submitter;
-# print "submitter=$ENV{'form.'.$_.':submitted_by'}= $submitter: ";
&submission($request,$ctr,$total);
$ctr++;
}
@@ -1253,24 +1829,38 @@ sub processHandGrade {
#---- Save the score and award for each student, if changed
sub saveHandGrade {
my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter) = @_;
- my %record=&Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname);
- my %newrecord;
+ my $usec = &Apache::lonnet::getsection($domain,$stuname,
+ $ENV{'request.course.id'});
+ if (!&canmodify($usec)) { return('not_allowed'); }
+ my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname);
+ my %newrecord = ();
+ my ($pts,$wgt) = ('','');
foreach (split(/:/,$ENV{'form.partlist'.$newflg})) {
- if ($ENV{'form.GD_SEL'.$newflg.'_'.$_} eq 'excused') {
- $newrecord{'resource.'.$_.'.solved'} = 'excused'
- if ($record{'resource.'.$_.'.solved'} ne 'excused');
- } else {
- my $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ?
- $ENV{'form.GD_BOX'.$newflg.'_'.$_} :
- $ENV{'form.RADVAL'.$newflg.'_'.$_});
- if ($pts eq '') {
- &userError($request,'No point was assigned for part '.$_.
- ' and for username '.$stuname.'.');
- return 'error';
+ my $dropMenu = $ENV{'form.GD_SEL'.$newflg.'_'.$_};
+ if ($dropMenu eq 'excused') {
+ if ($record{'resource.'.$_.'.solved'} ne 'excused') {
+ $newrecord{'resource.'.$_.'.solved'} = 'excused';
+ if (exists($record{'resource.'.$_.'.awarded'})) {
+ $newrecord{'resource.'.$_.'.awarded'} = '';
+ }
+ $newrecord{'resource.'.$_.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
}
- my $wgt = $ENV{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 :
+ } elsif ($dropMenu eq 'reset status'
+ && exists($record{'resource.'.$_.'.solved'})) { #don't bother if no old records -> no attempts
+ $newrecord{'resource.'.$_.'.tries'} = 0;
+ $newrecord{'resource.'.$_.'.solved'} = '';
+ $newrecord{'resource.'.$_.'.award'} = '';
+ $newrecord{'resource.'.$_.'.awarded'} = 0;
+ $newrecord{'resource.'.$_.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
+ } elsif ($dropMenu eq '') {
+ $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ?
+ $ENV{'form.GD_BOX'.$newflg.'_'.$_} :
+ $ENV{'form.RADVAL'.$newflg.'_'.$_});
+ return 'no_score' if ($pts eq '' && $ENV{'form.GD_SEL'.$newflg.'_'.$_} eq '');
+ $wgt = $ENV{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 :
$ENV{'form.WGT'.$newflg.'_'.$_};
my $partial= $pts/$wgt;
+ next if ($partial eq $record{'resource.'.$_.'.awarded'}); #do not update score for part if not changed.
$newrecord{'resource.'.$_.'.awarded'} = $partial
if ($record{'resource.'.$_.'.awarded'} ne $partial);
my $reckey = 'resource.'.$_.'.solved';
@@ -1283,15 +1873,15 @@ sub saveHandGrade {
}
$newrecord{'resource.'.$_.'.submitted_by'} = $submitter
if ($submitter && ($record{'resource.'.$_.'.submitted_by'} ne $submitter));
+ $newrecord{'resource.'.$_.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
}
}
if (scalar(keys(%newrecord)) > 0) {
- $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
&Apache::lonnet::cstore(\%newrecord,$symb,
$ENV{'request.course.id'},$domain,$stuname);
}
- return '';
+ return '',$pts,$wgt;
}
#--------------------------------------------------------------------------------------
@@ -1304,19 +1894,13 @@ sub viewgrades_js {
$request->print(<
- function viewOneStudent(user,domain) {
- document.onestudent.student.value = user;
- document.onestudent.userdom.value = domain;
- document.onestudent.submit();
- }
-
- function writePoint(partid,weight,point) {
- var radioButton = eval("document.classgrade.RADVAL_"+partid);
- var textbox = eval("document.classgrade.TEXTVAL_"+partid);
+ function writePoint(partid,weight,point) {
+ var radioButton = document.classgrade["RADVAL_"+partid];
+ var textbox = document.classgrade["TEXTVAL_"+partid];
if (point == "textval") {
- var point = eval("document.classgrade.TEXTVAL_"+partid+".value");
- if (isNaN(point) || point < 0) {
- alert("A number equal or greater than 0 is expected. Entered value = "+point);
+ point = document.classgrade["TEXTVAL_"+partid].value;
+ if (isNaN(point) || parseFloat(point) < 0) {
+ alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));
var resetbox = false;
for (var i=0; i weight) {
- var resp = confirm("You entered a value ("+point+
+ if (parseFloat(point) > parseFloat(weight)) {
+ var resp = confirm("You entered a value ("+parseFloat(point)+
") greater than the weight for the part. Accept?");
if (resp == false) {
textbox.value = "";
@@ -1339,21 +1923,19 @@ sub viewgrades_js {
}
for (var i=0; i weight) {
- var resp = confirm("You entered a value ("+point+
+ if (parseFloat(point) > parseFloat(weight)) {
+ var resp = confirm("You entered a value ("+parseFloat(point)+
") greater than the weight of the part. Accept?");
if (resp == false) {
textbox.value = "";
@@ -1430,19 +2010,21 @@ sub viewgrades_js {
}
function changeOneScore(partid,user) {
- var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_sv");
- if (selval[1].selected) {
- var boxval = eval("document.classgrade.GD_"+user+'_'+partid+"_aw");
- boxval.value = "";
+ var selval = document.classgrade["GD_"+user+'_'+partid+"_solved"];
+ if (selval[1].selected || selval[2].selected) {
+ document.classgrade["GD_"+user+'_'+partid+"_awarded"].value = "";
+ if (selval[2].selected) {
+ document.classgrade["GD_"+user+'_'+partid+"_tries"].value = "0";
+ }
}
}
function resetEntry(numpart) {
for (ctpart=0;ctpartManual Grading';
+ my $result='
Manual Grading
';
- $result.='Resource: '.$ENV{'form.url'}.''."\n";
+ $result.='Current Resource: '.$ENV{'form.probTitle'}.''."\n";
#view individual student submission form - called using Javascript viewOneStudent
- $result.= ''."\n";
+ $result.=&jscriptNform($url,$symb);
#beginning of class grading form
$result.= '');
+ foreach my $student (@skipped) { $request->print("$student \n"); }
+ }
+ if (@notallowed) {
+ $request->print('
Students Not Allowed to Modify
');
+ foreach my $student (@notallowed) { $request->print("$student \n"); }
}
- $request->print(&view_edit_entire_class_form($symb,$url));
+ $request->print(" \n");
$request->print(&show_grading_menu_form($symb,$url));
return '';
}
#------------- end of section for handling csv file upload ---------
#
#-------------------------------------------------------------------
+#
+#-------------- Next few routines handle grading by page/sequence
+#
+#--- Select a page/sequence and a student to grade
+sub pickStudentPage {
+ my ($request) = shift;
+
+ $request->print(<
+
+function checkPickOne(formname) {
+ if (radioSelection(formname.student) == null) {
+ alert("Please select the student you wish to grade.");
+ return;
+ }
+ ptr = pullDownSelection(formname.selectpage);
+ formname.page.value = formname["page"+ptr].value;
+ formname.title.value = formname["title"+ptr].value;
+ formname.submit();
+}
+
+
+LISTJAVASCRIPT
+ &commonJSfunctions($request);
+ my ($symb,$url) = &get_symb_and_url($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 $result='
'.
+ 'Manual Grading by Page or Sequence
';
+
+ $result.=''."\n";
+
+ $studentTable.=&show_grading_menu_form($symb,$url);
+ $request->print($studentTable);
+
+ return '';
+}
+
+sub getSymbMap {
+ my ($request) = @_;
+ my $navmap = Apache::lonnavmaps::navmap->new();
+
+ my %symbx = ();
+ my @titles = ();
+ my $minder = 0;
+
+ # Gather every sequence that has problems.
+ my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); }, 1);
+ for my $sequence ($navmap->getById('0.0'), @sequences) {
+ if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {
+ my $title = $minder.'.'.$sequence->compTitle();
+ push @titles, $title; # minder in case two titles are identical
+ $symbx{$title} = $sequence->symb();
+ $minder++;
+ }
+ }
+
+ $navmap->untieHashes();
+ return \@titles,\%symbx;
+}
-#-------------------------- Menu interface -------------------------
#
-#--- Show a Grading Menu button - Calls the next routine ---
-sub show_grading_menu_form {
- my ($symb,$url)=@_;
- my $result.='