--- loncom/homework/grades.pm 2002/10/04 06:22:12 1.54
+++ loncom/homework/grades.pm 2003/03/23 07:22:58 1.74
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.54 2002/10/04 06:22:12 albertel Exp $
+# $Id: grades.pm,v 1.74 2003/03/23 07:22:58 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -31,6 +31,8 @@
# 8/20 Gerd Kortemeyer
# Year 2002
# June-August H.K. Ng
+# Year 2003
+# February, March H.K. Ng
#
package Apache::grades;
@@ -39,11 +41,13 @@ use Apache::style;
use Apache::lonxml;
use Apache::lonnet;
use Apache::loncommon;
+use Apache::lonnavmaps;
use Apache::lonhomework;
+use Apache::loncoursedata;
use Apache::lonmsg qw(:user_normal_msg);
use Apache::Constants qw(:common);
-# ----- These first few routines are general use routines.-----
+# ----- These first few routines are general use routines.----
#
# --- Retrieve the parts that matches stores_\d+ from the metadata file.---
sub getpartlist {
@@ -72,13 +76,16 @@ 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;
}
@@ -107,62 +114,32 @@ sub response_type {
#--- 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'});
- my ($tmp) = keys(%classlist);
+ my $classlist=&Apache::loncoursedata::get_classlist();
# Bail out if we were unable to get the classlist
- return if ($tmp =~ /^(con_lost|error|no_such_host)/i);
-
- # 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{$_});
+ 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->{$_}};
# still a student?
- if (($hideexpired) && ($end) && ($end < $now)) {
- next;
- }
+ if (($hideexpired) && ($status ne 'Active')) {
+ 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;
- }
+ $sections{$section}++;
+ $fullnames{$_}=$fullname;
+ } else {
+ delete($classlist->{$_});
+ }
}
my %seen = ();
- foreach my $item (@holdsec) {
- push (@sections, $item) unless $seen{$item}++;
- }
- return (\%classlist,\@sections,\%allids,\%stusec,\%fullname);
-}
-
-# 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;
+ my @sections = sort(keys(%sections));
+ return ($classlist,\@sections,\%fullnames);
}
#find user domain
@@ -232,6 +209,9 @@ sub jscriptNform {
$jscript.= '
';
@@ -1161,9 +1282,7 @@ sub get_last_submission {
for ($version=1;$version<=$returnhash{'version'};$version++) {
foreach (sort(split(/\:/,$returnhash{$version.':keys'}))) {
$lasthash{$_}=$returnhash{$version.':'.$_};
- if ($returnhash{$version.':'.$_} =~ /(SUBMITTED|DRAFT)$/) {
$timestamp = scalar(localtime($returnhash{$version.':timestamp'}));
- }
}
}
foreach ((keys %lasthash)) {
@@ -1187,8 +1306,13 @@ 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;
}
+ # This is not really the right place to do this, but I cannot find a
+ # better one at this time. So here we go - the m in the s:::mg causes
+ # ^ to match the beginning of a new line. So we replace(???) the beginning
+ # of the line with to make things formatted a little better.
+ $string =~ s:^: :mg;
return $string;
}
@@ -1206,16 +1330,20 @@ sub processHandGrade {
while ($ctr < $ngrade) {
my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr});
my ($errorflag) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr);
+ if ($errorflag eq 'no_score') {
+ $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 =~ s/\s+/ /g;
$msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom,
$ENV{'form.msgsub'},$message);
}
@@ -1297,7 +1425,7 @@ sub processHandGrade {
$laststu = $firststu if ($ctr > $ngrade);
}
- my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0');
+ my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
my (@parsedlist,@nextlist);
my ($nextflg) = 0;
foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
@@ -1337,8 +1465,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++;
}
@@ -1359,12 +1485,17 @@ sub saveHandGrade {
my %newrecord;
foreach (split(/:/,$ENV{'form.partlist'.$newflg})) {
if ($ENV{'form.GD_SEL'.$newflg.'_'.$_} eq 'excused') {
- $newrecord{'resource.'.$_.'.solved'} = 'excused'
- if ($record{'resource.'.$_.'.solved'} ne 'excused');
+ if ($record{'resource.'.$_.'.solved'} ne 'excused') {
+ $newrecord{'resource.'.$_.'.solved'} = 'excused';
+ if (exists($record{'resource.'.$_.'.awarded'})) {
+ $newrecord{'resource.'.$_.'.awarded'} = '';
+ }
+ }
} else {
my $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 '');
my $wgt = $ENV{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 :
$ENV{'form.WGT'.$newflg.'_'.$_};
my $partial= $pts/$wgt;
@@ -1380,11 +1511,11 @@ 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);
}
@@ -1573,7 +1704,7 @@ sub viewgrades {
my ($symb,$url) = ($ENV{'form.symb'},$ENV{'form.url'});
my $result='
Manual Grading
';
- $result.='Resource: '.$ENV{'form.url'}.''."\n";
+ $result.='Problem: '.$ENV{'form.probTitle'}.''."\n";
#view individual student submission form - called using Javascript viewOneStudent
$result.=&jscriptNform($url,$symb);
@@ -1583,7 +1714,11 @@ sub viewgrades {
''."\n".
''."\n".
''."\n".
- ''."\n";
+ ''."\n".
+ ''."\n".
+ ''."\n".
+ ''."\n";
+
$result.='
Assign Common Grade To ';
if ($ENV{'form.section'} eq 'all') {
$result.='Class
';
-# $result.='To assign the same score for all the students use the radio buttons or '.
-# 'text box below. To assign scores individually fill in the score boxes for '.
-# 'each student in the table below. A part that has already '.
-# 'been graded does not get changed using the radio buttons or text box. '.
-# 'If needed, it has to be changed individually.';
-# $result.='
';
#radio buttons/text box for assigning points for a section or class.
#handles different parts of a problem
my ($partlist,$handgrade) = &response_type($ENV{'form.url'});
@@ -1636,7 +1765,7 @@ sub viewgrades {
$weight{$partid}.' (problem weight)
'."\n";
$result.= '
'.
+ $weight{$partid}.')"> '.
''.
'
'."\n";
$ctsparts++;
@@ -1678,7 +1807,7 @@ sub viewgrades {
#get info for each student
#list all the students - with points and grade status
- my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0');
+ my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
my $ctr = 0;
foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
my ($uname,$udom) = split(/:/);
@@ -1704,8 +1833,8 @@ sub viewstudentgrade {
''.$fullname.''.
'
'.$uname.'
'.$udom.'
'."\n";
- foreach my $part (@$parts) {
- my ($part,$type) = &split_part_type($part);
+ foreach my $apart (@$parts) {
+ my ($part,$type) = &split_part_type($apart);
my $score=$record{"resource.$part.$type"};
if ($type eq 'awarded') {
my $pts = $score eq '' ? '' : $score*$$weight{$part};
@@ -1749,9 +1878,8 @@ sub editgrades {
my $symb=$ENV{'form.symb'};
my $url =$ENV{'form.url'};
my $title='
'."\n".
+ &show_grading_menu_form ($symb,$url);
my $msg = 'Number of records updated = '.$rec_update.
' for '.$count.' student'.($count <= 1 ? '' : 's').'. '.
'Total number of students = '.$ENV{'form.total'}.' ';
@@ -1889,6 +2018,48 @@ sub split_part_type {
#
#-------------------------- Next few routines handles grading by csv upload
#
+#--- Menu to upload a csv scores ---
+sub upcsvScores_form {
+ my ($request) = shift;
+ my ($symb,$url)=&get_symb_and_url($request);
+ if (!$symb) {return '';}
+ my $result =<
+ function checkUpload(formname) {
+ if (formname.upfile.value == "") {
+ alert("Please use the browse button to select a file from your local directory.");
+ return false;
+ }
+ formname.submit();
+ if (navigator.appName !="Netscape") {self.close()}; //if netscape if appears to close before submit!!!
+ // any suggestion how to get around this??
+ }
+
+CSVFORMJS
+ $ENV{'form.probTitle'} = &Apache::lonnet::metadata($url,'title');
+ $result.='
'."\n";
+ $result.='
'."\n";
+ $result.=' Specify a file containing the class scores for problem - '.$ENV{'form.probTitle'}.
+ '.
'."\n";
+ return $result;
+}
+
#--- Javascript to handle csv upload
sub csvupload_javascript_reverse_associate {
return(<';
- $result.='
Resource: '.$url.'
';
+ $result.='
Problem: '.$ENV{'form.probTitle'}.'
';
my ($partlist,$handgrade) = &response_type($url);
my ($resptype,$hdgrade)=('','no');
for (sort keys(%$handgrade)) {
@@ -2004,6 +2175,9 @@ to this page if the data selected is ins
value="$ENV{'form.upfile_associate'}" />
+
+
+
+LISTJAVASCRIPT
+
+ 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='
';
+
+ my ($depth,$ctr,$question) = (1,0,1);
+ $iterator->next(); # skip the first BEGIN_MAP
+ my $curRes = $iterator->next(); # for "current resource"
+ while ($depth > 0 && $ctr < 100) { # ctr, just in case it never gets out of loop
+ if($curRes == $iterator->BEGIN_MAP) { $depth++; }
+ if($curRes == $iterator->END_MAP) { $depth++; }
+
+ if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
+ my $parts = $curRes->parts();
+ $parts = &temp_parts_fix($parts); # remove line when lonnavmap is fixed
+ my $title = $curRes->compTitle();
+ my $symbx = $curRes->symb();
+ $studentTable.='
/g;
+ $studentTable.=' '.$title.' Correct answer: '.$companswer;
+ }
+
+ my %record = &Apache::lonnet::restore($symbx,$ENV{'request.course.id'},$udom,$uname);
+
+ if ($ENV{'form.lastSub'} eq 'datesub') {
+ if ($record{'version'} eq '') {
+ $studentTable.=' No recorded submission for this problem ';
+ } else {
+ $studentTable.='
'.
+ '
'.
+ '
Date/Time
'.
+ '
Submission
'.
+ '
Status
';
+ my ($version);
+ for ($version=1;$version<=$record{'version'};$version++) {
+ my $timestamp = scalar(localtime($record{$version.':timestamp'}));
+ $studentTable.='
'."\n";
+
+ $request->print($result);
+
+ my $navmap = Apache::lonnavmaps::navmap-> new($request,
+ $ENV{'request.course.fn'}.'.db',
+ $ENV{'request.course.fn'}.'_parms.db',1, 1);
+ my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'});
+ my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
+
+ my $iterator = $navmap->getIterator($map->map_start(),
+ $map->map_finish());
+
+ my $studentTable='
'.
+ '
'.
+ '
No
'.
+ '
Title
'.
+ '
Previous Score
'.
+ '
New Score
';
+
+ $iterator->next(); # skip the first BEGIN_MAP
+ my $curRes = $iterator->next(); # for "current resource"
+ my ($depth,$ctr,$question,$changeflag)= (1,0,1,0);
+ while ($depth > 0 && $ctr < 100) { # ctr, just in case it never gets out of loop
+ if($curRes == $iterator->BEGIN_MAP) { $depth++; }
+ if($curRes == $iterator->END_MAP) { $depth++; }
+
+ if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
+ my $parts = $curRes->parts();
+ $parts = &temp_parts_fix($parts); # remove line when lonnavmap is fixed
+ my $title = $curRes->compTitle();
+ my $symbx = $curRes->symb();
+ $studentTable.='
'."\n";
@@ -2155,9 +2789,72 @@ sub gradingmenu {
my ($request) = @_;
my ($symb,$url)=&get_symb_and_url($request);
if (!$symb) {return '';}
- my $result='
Select a Grading Method
';
- $result.='
';
- $result.='
Resource: '.$url.'
';
+
+ $request->print(<
+ function checkChoice(formname) {
+ var cmd = formname.command;
+ formname.lastCmd.value = radioSelection(formname.command);
+ formname.lastSec.value = pullDownSelection(formname.section);
+ if (cmd[0].checked || cmd[1].checked || cmd[2].checked) {
+ formname.submit();
+ }
+
+ if (cmd[3].checked) {
+ var url = "/adm/grades?command=csvform&symb="+formname.symb.value+"&url="+formname.url.value;
+ var options = "width=620,height=340,screenx=70,screeny=75,";
+ options += "resizable=no,scrollbars=no,status=no,";
+ options += "menubar=no,toolbar=no,location=no,directories=no";
+ var newWin = window.open(url, "CSVFile", options);
+ newWin.focus();
+ }
+
+ if (cmd[4].checked) {
+ if (!checkReceiptNo(formname,'notOK')) { return false;}
+ formname.submit();
+ }
+ }
+
+ function checkReceiptNo(formname,nospace) {
+ var receiptNo = formname.receipt.value;
+ var checkOpt = false;
+ if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;}
+ if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;}
+ if (checkOpt) {
+ alert("Please enter a receipt number given by a student in the receipt box.");
+ formname.receipt.value = "";
+ formname.receipt.focus();
+ return false;
+ }
+ formname.command[4].checked = true;
+ return true;
+ }
+
+ function radioSelection(radioButton) {
+ var selection=null;
+ for (var i=0; i
+GRADINGMENUJS
+
+ my $probTitle = &Apache::lonnet::metadata($ENV{'form.url'},'title');
+ my $result='
Manual Grading/View Submission
'.
+ '
'.
+ '
Problem: '.$probTitle.'
';
my ($partlist,$handgrade) = &response_type($url);
my ($resptype,$hdgrade)=('','no');
for (sort keys(%$handgrade)) {
@@ -2169,155 +2866,79 @@ sub gradingmenu {
'
Handgrade: '.$handgrade.'
';
}
$result.='
';
- $result.=&view_edit_entire_class_form($symb,$url).' ';
- $result.=&upcsvScores_form($symb,$url).' ';
- $result.=&viewGradeaStu_form($symb,$url,$resptype,$hdgrade).' ';
- $result.=&verifyReceipt_form($symb,$url)
- if ((&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})) && ($symb));
-
- return $result;
-}
-#--- Menu for grading a section or the whole class ---
-sub view_edit_entire_class_form {
- my ($symb,$url)=@_;
my ($classlist,$sections) = &getclasslist('all','0');
- my $result.='
'."\n";
- $result.='
'."\n";
- $result.=' Grade Entire Section or Class
'."\n";
- return $result;
-}
-
-#--- Form to input a receipt number ---
-sub verifyReceipt_form {
- my ($symb,$url) = @_;
- my $result = ''."\n";
-
- my $hostver=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'});
-
- $result.='
'."\n";
- $result.='
'."\n";
- $result.=' Verify a Submission Receipt Issued by this Server