--- loncom/homework/grades.pm 2002/07/07 20:08:45 1.36
+++ loncom/homework/grades.pm 2003/10/13 22:36:59 1.130.2.1.2.8
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.36 2002/07/07 20:08:45 ng Exp $
+# $Id: grades.pm,v 1.130.2.1.2.8 2003/10/13 22:36:59 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -30,7 +30,10 @@
# 7/26 H.K. Ng
# 8/20 Gerd Kortemeyer
# Year 2002
-# June, July 2002 H.K. Ng
+# June-August H.K. Ng
+# Year 2003
+# February, March H.K. Ng
+# July, H. K. Ng
#
package Apache::grades;
@@ -39,370 +42,823 @@ 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;
-sub moreinfo {
- my ($request,$reason) = @_;
- $request->print("Unable to process request: $reason");
- if ( $Apache::grades::viewgrades eq 'F' ) {
- $request->print('
');
- }
- return '';
-}
+my %oldessays=();
+my %perm=();
-sub verifyreceipt {
- my $request=shift;
- my $courseid=$ENV{'request.course.id'};
-# my $cdom=$ENV{"course.$courseid.domain"};
-# my $cnum=$ENV{"course.$courseid.num"};
- my $receipt=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'.
- $ENV{'form.receipt'};
- $receipt=~s/[^\-\d]//g;
- my $symb=$ENV{'form.symb'};
- unless ($symb) {
- $symb=&Apache::lonnet::symbread($ENV{'form.url'});
- }
- if ((&Apache::lonnet::allowed('mgr',$courseid)) && ($symb)) {
- $request->print('
Verifying Submission Receipt '.$receipt.'
');
- my $matches=0;
- my ($classlist) = &getclasslist('all','0');
- foreach my $student ( sort(@{ $$classlist{'all'} }) ) {
- my ($uname,$udom)=split(/\:/,$student);
- if ($receipt eq
- &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) {
- $request->print('Matching '.$student.' ');
- $matches++;
- }
- }
- $request->printf('
'.$matches." match%s
",$matches <= 1 ? '' : 'es');
-# needs to print who is matched
+# ----- These first few routines are general use routines.----
+#
+# --- Retrieve the parts that matches stores_\d+ from the metadata file.---
+sub getpartlist {
+ my ($url) = @_;
+ my @parts =();
+ my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
+ foreach my $key (@metakeys) {
+ if ( $key =~ m/stores_(\w+)_.*/) {
+ push(@parts,$key);
+ }
}
- return '';
+ return @parts;
}
-sub student_gradeStatus {
- my ($url,$udom,$uname) = @_;
- my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
- my %record= &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
- foreach my $part (&getpartlist($url)) {
- my ($temp,$part,$type)=split(/_/,$part);
- if ($type eq 'solved') {
- my ($status,$foo)=split(/_/,$record{"resource.$part.$type"},2);
- $status = 'partial' if ($foo =~ /^partially/);
- $status = 'nothing' if ($status eq '');
- return $type,$status;
- }
- }
- return '';
+# --- Get the symbolic name of a problem and the url
+sub get_symb_and_url {
+ my ($request) = @_;
+ (my $url=$ENV{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
+ my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
+ if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
+ return ($symb,$url);
}
+# --- Retrieve the fullname for a user. Return lastname, first middle ---
+# --- Generation is attached next to the lastname if it exists. ---
sub get_fullname {
- my ($sname,$sdom) = @_;
+ my ($uname,$udom) = @_;
my %name=&Apache::lonnet::get('environment', ['lastname','generation',
'firstname','middlename'],
- $sdom,$sname);
+ $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;
}
-sub listStudents {
- my ($request) = shift;
- my $cdom =$ENV{"course.$ENV{'request.course.id'}.domain"};
- my $cnum =$ENV{"course.$ENV{'request.course.id'}.num"};
- my $getsec =$ENV{'form.section'};
- my $submitonly=$ENV{'form.submitonly'};
-
- $request->print(< View Submissions for a Student or a Group of Students
- Resource: $ENV{'form.url'}
-
- View Problem: no
- yes
- Submissions: last
- all
-
-
-
-
-
-
-
Select
Username
-
Fullname
Domain
-
Grade Status
-ENDTABLEST
- if ($ENV{'form.url'}) {
- $request->print(''."\n");
- }
- if ($ENV{'form.symb'}) {
- $request->print(''."\n");
- }
- $request->print(''."\n");
-
- my ($classlist) = &getclasslist($getsec,'0');
- foreach my $student ( sort(@{ $$classlist{$getsec} }) ) {
- my ($sname,$sdom) = split(/:/,$student);
- my ($type,$status) = &student_gradeStatus($ENV{'form.url'},$cdom,$sname);
- next if ($status eq 'nothing' && $submitonly eq 'yes');
-
- my $fullname = &get_fullname($sname,$sdom);
- if ( $Apache::grades::viewgrades eq 'F' ) {
- $request->print("\n".'
'.
- '
'."\n".
- '
'.$sname.'
'."\n".
- '
'.$fullname.'
'."\n".
- '
'.$sdom.'
'."\n");
- $request->print('
'.$status.'
'."\n");
+#--- 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.')').')';
+ }
+}
- $request->print('
');
- }
- }
- $request->print('
');
- $request->print('');
+#--- 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,$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_\w+.*/) {
+ my ($responsetype,$part) = split(/_/,$_,2);
+ my ($partid,$respid) = split(/_/,$part);
+ $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;
+ }
+ }
+ return \@partlist,\%handgrade;
}
-sub processGroup {
- my ($request) = shift;
- my $ctr = 0;
- my @stuchecked = (ref($ENV{'form.stuinfo'}) ? @{$ENV{'form.stuinfo'}}
- : ($ENV{'form.stuinfo'}));
- my $total = scalar(@stuchecked)-1;
- if ($stuchecked[0] eq '') {
- &userError($request,'No student was selected for viewing/grading.');
- return;
- }
- foreach (@stuchecked) {
- my ($sname,$sdom,$fullname) = split(/:/);
- $ENV{'form.student'} = $sname;
- $ENV{'form.fullname'} = $fullname;
- &submission($request,$ctr,$total);
- $ctr++;
- }
- return 'The End';
+#--- 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.='
'."\n");
- $request->print('Step: '.($step ne '' ? $step : 'Use your browser back button to correct')
- .'
'."\n");
- return '';
+#--- 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;
}
-#FIXME - needs to handle multiple matches
-sub finduser {
- my ($name) = @_;
- my $domain = '';
- if ( $Apache::grades::viewgrades eq 'F' ) {
- my ($classlist) = &getclasslist('all','0');
- foreach ( sort(@{ $$classlist{'all'} }) ) {
- my ($posname,$posdomain) = split(/:/);
- if ($posname =~ $name) { $name=$posname; $domain=$posdomain; last; }
- }
- return ($name,$domain);
- } else {
- return ($ENV{'user.name'},$ENV{'user.domain'});
- }
+#-- 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 ($coursedomain,$coursenum) = split(/_/,$ENV{'request.course.id'});
- my %classlist=&Apache::lonnet::dump('classlist',$coursedomain,$coursenum);
- my $now = time;
- my (@holdsec,@sections);
- foreach my $student (keys(%classlist)) {
- my ($end,$start)=split(/:/,$classlist{$student});
- # still a student?
- if (($hideexpired) && ($end) && ($end < $now)) {
- next;
- }
- my ($unam,$udom) = split(/:/,$student,2);
- my $section = &Apache::lonnet::usection($udom,$unam,$ENV{'request.course.id'});
- $section = ($section ne '-1' ? $section : 'no');
- push @holdsec,$section;
- push (@{ $classlist{$getsec} }, $student) if ($getsec eq 'all' || $getsec eq $section);
- }
- my %seen = ();
- foreach my $item (@holdsec) {
- push (@sections, $item) unless $seen{$item}++;
- }
- return (\%classlist,\@sections);
+ 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');
+ if (&canview($section)) {
+ if ($getsec eq 'all' || $getsec eq $section) {
+ $sections{$section}++;
+ $fullnames{$_}=$fullname;
+ } else {
+ delete($classlist->{$_});
+ }
+ } else {
+ delete($classlist->{$_});
+ }
+ }
+ my %seen = ();
+ my @sections = sort(keys(%sections));
+ return ($classlist,\@sections,\%fullnames);
}
-sub getpartlist {
- my ($url) = @_;
- my @parts =();
- my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
- foreach my $key (@metakeys) {
- if ( $key =~ m/stores_([0-9]+)_.*/) {
- push(@parts,$key);
+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;
+ }
+ }
}
- }
- return @parts;
+ #can't modify
+ return 0;
}
-sub viewstudentgrade {
- my ($url,$symb,$courseid,$student,@parts) = @_;
- my $result ='';
- my $cellclr = '"#ffffdd"';
- my ($username,$domain) = split(/:/,$student);
-
- my $fullname = &get_fullname($username,$domain);
- my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$username);
-
- $result.="
'."\n";
+
+ $matches++;
+ }
+ }
+ if ($matches == 0) {
+ $string = $title.'No match found for the above receipt.';
+ } else {
+ $string = &jscriptNform($url,$symb).$title.
+ 'The above receipt matches the following student'.
+ ($matches <= 1 ? '.' : 's.')."\n".
+ '
'."\n".
+ '
'."\n".
+ '
Fullname
'."\n".
+ '
Username
'."\n".
+ '
Domain
'."\n".
+ $contents.
+ '
'."\n";
+ }
+ return $string.&show_grading_menu_form($symb,$url);
+}
+
+#--- This is called by a number of programs.
+#--- Called from the Grading Menu - View/Grade an individual student
+#--- Also called directly when one clicks on the subm button
+# 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 $viewgrade = $ENV{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View';
+ $ENV{'form.probTitle'} = $ENV{'form.probTitle'} eq '' ?
+ &Apache::lonnet::gettitle($symb) : $ENV{'form.probTitle'};
+
+ my $result='
'.$viewgrade.
+ ' Submissions for a Student or a Group of Students
';
+
+ my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$ENV{'form.probTitle'});
+ $result.=$table;
+
+ $request->print(<
+ function checkSelect(checkBox) {
+ var ctr=0;
+ var sense="";
+ if (checkBox.length > 1) {
+ for (var i=0; i
+LISTJAVASCRIPT
+
+ &commonJSfunctions($request);
+ $request->print($result);
+
+ my $checkhdgrade = ($ENV{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked' : '';
+ my $checklastsub = $checkhdgrade eq '' ? 'checked' : '';
+ my $gradeTable=''."\n";
+ if ($ctr == 0) {
+ my $num_students=(scalar(keys(%$fullname)));
+ if ($num_students eq 0) {
+ $gradeTable=' There are no students currently enrolled.';
+ } else {
+ $gradeTable=' '.
+ 'No submissions found for this resource for any students. ('.$num_students.
+ ' checked for submissions ';
+ }
+ } elsif ($ctr == 1) {
+ $gradeTable =~ s/type=checkbox/type=checkbox checked/;
+ }
+ $gradeTable.=&show_grading_menu_form($symb,$url);
+ $request->print($gradeTable);
+ return '';
+}
+
+#---- Called from the listStudents routine
+# Displays the submissions for one student or a group of students
+sub processGroup {
+ my ($request) = shift;
+ my $ctr = 0;
+ my @stuchecked = (ref($ENV{'form.stuinfo'}) ? @{$ENV{'form.stuinfo'}}
+ : ($ENV{'form.stuinfo'}));
+ my $total = scalar(@stuchecked)-1;
+
+ foreach (@stuchecked) {
+ my ($uname,$udom,$fullname) = split(/:/);
+ $ENV{'form.student'} = $uname;
+ $ENV{'form.userdom'} = $udom;
+ $ENV{'form.fullname'} = $fullname;
+ &submission($request,$ctr,$total);
+ $ctr++;
+ }
+ return '';
+}
+
+#------------------------------------------------------------------------------------
+#
+#-------------------------- Next few routines handles grading by student, essentially
+# handles essay response type problem/part
+#
+#--- Javascript to handle the submission page functionality ---
+sub sub_page_js {
+ my $request = shift;
+ $request->print(<
- function updateRadio(radioButton,formtextbox,formsel,wgt) {
- var pts = formtextbox.value;
- var resetbox =false;
- if (isNaN(pts) || pts < 0) {
- alert("A number equal or greater than 0 is expected. Entered value = "+pts);
+ function updateRadio(formname,id,weight) {
+ var gradeBox = formname["GD_BOX"+id];
+ var radioButton = formname["RADVAL"+id];
+ var oldpts = formname["oldpts"+id].value;
+ var pts = checkSolved(formname,id) == 'update' ? gradeBox.value : oldpts;
+ gradeBox.value = pts;
+ var resetbox = false;
+ if (isNaN(pts) || pts < 0) {
+ alert("A number equal or greater than 0 is expected. Entered value = "+pts);
+ for (var i=0; i weight) {
+ var resp = confirm("You entered a value ("+pts+
+ ") greater than the weight for the part. Accept?");
+ if (resp == false) {
+ gradeBox.value = oldpts;
+ return;
+ }
+ }
+
for (var i=0; i 0 && pts < wgt) {
- formsel[4].selected = true;
+
+//======= Check that a score is assigned for all the problems (page/sequence grading only) =========
+ function checkSubmitPage(formname,total) {
+ noscore = new Array(100);
+ var ptr = 0;
+ for (i=1;i
+SUBJAVASCRIPT
+}
+
+#--- javascript for essay type problem --
+sub sub_page_kw_js {
+ my $request = shift;
+ my $iconpath = $request->dir_config('lonIconsURL');
+ &commonJSfunctions($request);
+ $request->print(<
+
+//===================== Show list of keywords ====================
+ function keywords(formname) {
+ var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",formname.keywords.value);
+ if (nret==null) return;
+ formname.keywords.value = nret;
+
+ if (formname.keywords.value != "") {
+ formname.refresh.value = "on";
+ formname.submit();
}
return;
}
- function keywords(keyform) {
- var keywds = keyform.value;
- var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",keywds);
- if (nret==null) return;
- keyform.value = nret;
+//===================== Script to view submitted by ==================
+ function viewSubmitter(submitter) {
+ document.SCORE.refresh.value = "on";
+ document.SCORE.NCT.value = "1";
+ document.SCORE.unamedom0.value = submitter;
+ document.SCORE.submit();
return;
}
@@ -413,115 +869,174 @@ sub submission {
else return;
var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
if (cleantxt=="") {
- alert("Select a word or group of words from document and then click this link.");
+ alert("Please select a word or group of words from document and then click this link.");
return;
}
- var nret = prompt("Add selection to keyword list?",cleantxt);
+ var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);
if (nret==null) return;
- var curlist = document.SCORE.keywords.value;
- document.SCORE.keywords.value = curlist+" "+nret;
+ document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;
+ if (document.SCORE.keywords.value != "") {
+ document.SCORE.refresh.value = "on";
+ document.SCORE.submit();
+ }
return;
}
//====================== Script for composing message ==============
+ // preload images
+ img1 = new Image();
+ img1.src = "$iconpath/mailbkgrd.gif";
+ img2 = new Image();
+ img2.src = "$iconpath/mailto.gif";
+
function msgCenter(msgform,usrctr,fullname) {
var Nmsg = msgform.savemsgN.value;
savedMsgHeader(Nmsg,usrctr,fullname);
var subject = msgform.msgsub.value;
- displaySubject(subject);
+ var msgchk = document.SCORE["includemsg"+usrctr].value;
+ re = /msgsub/;
+ var shwsel = "";
+ if (re.test(msgchk)) { shwsel = "checked" }
+ subject = (document.SCORE.shownSub.value == 0 ? checkEntities(subject) : subject);
+ displaySubject(checkEntities(subject),shwsel);
for (var i=1; i<=Nmsg; i++) {
- var message = eval("document.SCORE.savemsg"+i+".value");
- displaySavedMsg(i,message);
+ var testmsg = "savemsg"+i+",";
+ re = new RegExp(testmsg,"g");
+ shwsel = "";
+ if (re.test(msgchk)) { shwsel = "checked" }
+ var message = document.SCORE["savemsg"+i].value;
+ message = (document.SCORE["shownOnce"+i].value == 0 ? checkEntities(message) : message);
+ displaySavedMsg(i,message,shwsel); //I do not get it. w/o checkEntities on saved messages,
+ //any < is already converted to <, etc. However, only once!!
}
- newmsg = eval("document.SCORE.newmsg"+usrctr+".value");
- newMsg(newmsg);
+ newmsg = document.SCORE["newmsg"+usrctr].value;
+ shwsel = "";
+ re = /newmsg/;
+ if (re.test(msgchk)) { shwsel = "checked" }
+ newMsg(newmsg,shwsel);
msgTail();
return;
}
-function savedMsgHeader(Nmsg,usrctr,fullname) {
- var height = 30*Nmsg+250;
+ function checkEntities(strx) {
+ if (strx.length == 0) return strx;
+ var orgStr = ["&", "<", ">", '"'];
+ var newStr = ["&", "<", ">", """];
+ var counter = 0;
+ while (counter < 4) {
+ strx = strReplace(strx,orgStr[counter],newStr[counter]);
+ counter++;
+ }
+ return strx;
+ }
+
+ function strReplace(strx, orgStr, newStr) {
+ return strx.split(orgStr).join(newStr);
+ }
+
+ function savedMsgHeader(Nmsg,usrctr,fullname) {
+ var height = 70*Nmsg+250;
var scrollbar = "no";
if (height > 600) {
height = 600;
scrollbar = "yes";
}
- pWin = window.open('', 'MessageCenter', 'toolbar=no,location=no,scrollbars='+scrollbar+',width=600,height='+height);
- pWin.document.write("");
- pWin.document.write("Message Central");
-
- pWin.document.write("
SUBJAVASCRIPT
}
- (my $url=$ENV{'form.url'})=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
- if ($ENV{'form.student'} eq '') { &moreinfo($request,'Need student login id'); return ''; }
- my ($uname,$udom) = &finduser($ENV{'form.student'});
- if ($uname eq '') { &moreinfo($request,'Unable to find student'); return ''; }
-
- my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
- if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
- my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');
-#
-# header info
- if ($counter == 0) {
- $request->print('
Submission Record
'.
- ' Resource: '.$url.'');
- }
+#--- displays the grading box, used in essay type problem and grading by page/sequence
+sub gradeBox {
+ my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;
+
+ my $checkIcon = '';
+
+ my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);
+ my $wgtmsg = ($wgt > 0 ? '(problem weight)' :
+ 'problem weight assigned by computer');
+ $wgt = ($wgt > 0 ? $wgt : '1');
+ my $score = ($$record{'resource.'.$partid.'.awarded'} eq '' ?
+ '' : $$record{'resource.'.$partid.'.awarded'}*$wgt);
+ my $result=''."\n";
+
+ $result.='
'.
+ 'Part '.$partid.' Points:
'."\n";
+
+ my $ctr = 0;
+ $result.='
'."\n"; # display radio buttons in a nice table 10 across
+ while ($ctr<=$wgt) {
+ $result.= '
';
+ return $result;
+}
+
+# --------------------------- show submissions of a student, option to grade
+sub submission {
+ my ($request,$counter,$total) = @_;
+
+ (my $url=$ENV{'form.url'})=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
+ my ($uname,$udom) = ($ENV{'form.student'},$ENV{'form.userdom'});
+ $udom = ($udom eq '' ? $ENV{'user.domain'} : $udom); #has form.userdom changed for a student?
+ my $usec = &Apache::lonnet::getsection($udom,$uname,$ENV{'request.course.id'});
+ $ENV{'form.fullname'} = &get_fullname ($uname,$udom) if $ENV{'form.fullname'} eq '';
+
+ my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
+ if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
+
+ if (!&canview($usec)) {
+ $request->print('Unable to view requested student.('.
+ $uname.$udom.$usec.$ENV{'request.course.id'}.')');
+ $request->print(&show_grading_menu_form($symb,$url));
+ return;
+ }
+
+ $ENV{'form.lastSub'} = ($ENV{'form.lastSub'} eq '' ? 'datesub' : $ENV{'form.lastSub'});
+ my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');
+ my $checkIcon = '';
+
+ # header info
+ if ($counter == 0) {
+ &sub_page_js($request);
+ &sub_page_kw_js($request) if ($ENV{'form.handgrade'} eq 'yes');
+ $ENV{'form.probTitle'} = $ENV{'form.probTitle'} eq '' ?
+ &Apache::lonnet::gettitle($symb) : $ENV{'form.probTitle'};
+
+ $request->print('
Submission Record
'."\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' 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 '' && $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'});
+
+ 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.msgsub'} = $keyhash{$symb.'_subject'} ne '' ?
+ $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('';
- $request->print($endform);
- }
- return '';
+ my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
+
+ my ($partlist,$handgrade) = &response_type($url,$symb);
+
+ # Display student info
+ $request->print(($counter == 0 ? '' : ' '));
+ my $result='
'."\n".
+ '
'."\n";
+
+ $result.='Fullname: '.&nameUserString(undef,$ENV{'form.fullname'},$uname,$udom).' '."\n";
+ $result.=''."\n";
+
+ # If any part of the problem is an essay-response (handgraded), then check for collaborators
+ my @col_fullnames;
+ my ($classlist,$fullname);
+ if ($ENV{'form.handgrade'} eq 'yes') {
+ ($classlist,undef,$fullname) = &getclasslist('all','0');
+ for (keys (%$handgrade)) {
+ my $ncol = &Apache::lonnet::EXT('resource.'.$_.
+ '.maxcollaborators',
+ $symb,$udom,$uname);
+ next if ($ncol <= 0);
+ s/\_/\./g;
+ next if ($record{'resource.'.$_.'.collaborators'} eq '');
+ my @goodcollaborators = ();
+ my @badcollaborators = ();
+ foreach (split(/,?\s+/,$record{'resource.'.$_.'.collaborators'})) {
+ $_ =~ s/[\$\^\(\)]//g;
+ next if ($_ eq '');
+ my ($co_name,$co_dom) = split /\@|:/,$_;
+ $co_dom = $udom if (! defined($co_dom) || $co_dom =~ /^domain$/i);
+ next if ($co_name eq $uname && $co_dom eq $udom);
+ # Doing this grep allows 'fuzzy' specification
+ my @Matches = grep /^$co_name:$co_dom$/i,keys %$classlist;
+ if (! scalar(@Matches)) {
+ push @badcollaborators,$_;
+ } else {
+ push @goodcollaborators, @Matches;
+ }
+ }
+ if (scalar(@goodcollaborators) != 0) {
+ $result.='Collaborators: ';
+ foreach (@goodcollaborators) {
+ my ($lastname,$givenn) = split(/,/,$$fullname{$_});
+ push @col_fullnames, $givenn.' '.$lastname;
+ $result.=$$fullname{$_}.' ';
+ }
+ $result.=' '."\n";
+ $result.=''."\n";
+ }
+ if (scalar(@badcollaborators) > 0) {
+ $result.='
';
+ $result .= 'This student has submitted too many '.
+ 'collaborators. Maximum is '.$ncol.'.';
+ $result .= '
';
+ }
+ }
+ }
+ $request->print($result."\n");
+
+ # print student answer/submission
+ # Options are (1) Handgaded submission only
+ # (2) Last submission, includes submission that is not handgraded
+ # (for multi-response type part)
+ # (3) Last submission plus the parts info
+ # (4) The whole record for this student
+ if ($ENV{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {
+ if ($ENV{'form.'.$uname.':'.$udom.':submitted_by'}) {
+ my $submitby=''.
+ 'Collaborative submission by: '.
+ ''.
+ $$fullname{$ENV{'form.'.$uname.':'.$udom.':submitted_by'}}.'';
+ $request->print($submitby);
+ } else {
+ my ($string,$timestamp)= &get_last_submission (\%record);
+ my $lastsubonly=''.
+ ($$timestamp eq '' ? '' : 'Date Submitted: '.
+ $$timestamp)."
\n";
+ if ($$timestamp eq '') {
+ $lastsubonly.='
'.$$string[0];
+ } else {
+ for my $part (sort keys(%$handgrade)) {
+ my ($responsetype,$foo) = split(/:/,$$handgrade{$part});
+ my ($partid,$respid) = split(/_/,$part);
+ if (!exists($record{'resource.'.$partid.'.'.$respid.'.submission'})) {
+ $lastsubonly.='
Part '.
+ $partid.' ( ID '.$respid.
+ ' ) '.
+ 'Nothing submitted - no attempts
';
+ } else {
+ foreach (@$string) {
+ my ($partid,$respid) = /^resource\.(\w+)\.(\w+)\.submission/;
+ if ($part eq ($partid.'_'.$respid)) {
+ my ($ressub,$subval) = split(/:/,$_,2);
+ # Similarity check
+ my $similar='';
+ my ($oname,$odom,$ocrsid,$oessay,$osim)=&most_similar($uname,$udom,$subval);
+ if ($osim) {
+ $osim=int($osim*100.0);
+ $similar='
Essay is '.$osim.
+ '% similar to an essay by '.&Apache::loncommon::plainname($oname,$odom).
+ '
'.
+ &keywords_highlight($oessay).'
';
+ }
+ $lastsubonly.='
Part '.
+ $partid.' ( ID '.$respid.
+ ' ) '.
+ ($record{"resource.$partid.$respid.uploadedurl"}?
+ ' File uploaded by student '.
+ 'Like all files provided by users, '.
+ 'this file may contain virusses ':'').
+ 'Submitted Answer: '.
+ &cleanRecord($subval,$responsetype,$symb).
+ '
'."\n";
- return $result;
-}
+ $request->print(<
+ function writePoint(partid,weight,point) {
+ var radioButton = document.classgrade["RADVAL_"+partid];
+ var textbox = document.classgrade["TEXTVAL_"+partid];
+ if (point == "textval") {
+ 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 parseFloat(weight)) {
+ var resp = confirm("You entered a value ("+parseFloat(point)+
+ ") greater than the weight for the part. Accept?");
+ if (resp == false) {
+ textbox.value = "";
+ return;
+ }
+ }
+ for (var i=0; i
'."\n";
- $result.='
'."\n";
- $result.=' Specify a file containing the class scores for above resource
'."\n";
- return $result;
-}
-
-sub viewGradeaStu_form {
- my ($symb,$url,$response,$handgrade) = @_;
- my ($classlist,$sections) = &getclasslist('all','0');
- my $result.='
'."\n";
- $result.='
'."\n";
- $result.=' View/Grade an Individual Student\'s Submission
'."\n";
- $result.='
'."\n";
- $result.=''."\n";
- $result.='
'."\n";
- $result.='
'."\n";
- return $result;
-}
-
-sub verifyReceipt_form {
- my ($symb,$url) = @_;
- my $cdom=$ENV{"course.$ENV{'request.course.id'}.domain"};
- my $cnum=$ENV{"course.$ENV{'request.course.id'}.num"};
- my $hostver=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'});
-
- my $result.='
'."\n";
- $result.='
'."\n";
- $result.=' Verify a Submission Receipt Issued by this Server
'."\n";
- $result.='
'."\n";
- $result.='';
- $result.='
'."\n";
- $result.='
'."\n";
- return $result;
+ } else {
+ textbox.value = parseFloat(point);
+ }
+ for (i=0;i parseFloat(weight)) {
+ var resp = confirm("You entered a value ("+parseFloat(point)+
+ ") greater than the weight of the part. Accept?");
+ if (resp == false) {
+ textbox.value = "";
+ return;
+ }
+ }
+ selval[0].selected = true;
+ }
+
+ function changeOneScore(partid,user) {
+ 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;ctpart
+VIEWJAVASCRIPT
}
+#--- show scores for a section or whole class w/ option to change/update a score
sub viewgrades {
- my ($request) = @_;
- my $result='';
+ my ($request) = shift;
+ &viewgrades_js($request);
- #get resource reference
- my ($symb,$url)=&get_symb_and_url($request);
- if (!$symb) {return '';}
- #get classlist
- my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
- #print "Found $cdom:$cnum ";
- my ($classlist) = &getclasslist('all','0');
- my $headerclr = '"#ccffff"';
- my $cellclr = '"#ffffcc"';
-
- #get list of parts for this problem
- my (@parts) = sort(&getpartlist($url));
-
- $request->print ("
Manual Grading
");
-
- #start the form
- $result = '';
- $result.=&show_grading_menu_form($symb,$url);
- return $result;
+ ''."\n".
+ ''."\n".
+ ''."\n".
+ ''."\n";
+
+ my $sectionClass;
+ if ($ENV{'form.section'} eq 'all') {
+ $sectionClass='Class ';
+ } elsif ($ENV{'form.section'} eq 'no') {
+ $sectionClass='Students in no Section ';
+ } else {
+ $sectionClass='Students in Section '.$ENV{'form.section'}.'';
+ }
+ $result.='
Assign Common Grade To '.$sectionClass;
+ $result.= '
'."\n".
+ '
';
+ #radio buttons/text box for assigning points for a section or class.
+ #handles different parts of a problem
+ my ($partlist,$handgrade) = &response_type($url,$symb);
+ my %weight = ();
+ my $ctsparts = 0;
+ $result.='
';
+ my %seen = ();
+ for (sort keys(%$handgrade)) {
+ my ($partid,$respid) = split (/_/,$_,2);
+ next if $seen{$partid};
+ $seen{$partid}++;
+ my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
+ my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
+ $weight{$partid} = $wgt eq '' ? '1' : $wgt;
+
+ $result.=''."\n";
+ $result.=''."\n";
+ $result.='
Part '.$partid.' Point:
';
+ $result.='
';
+ my $ctr = 0;
+ while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across
+ $result.= '
'.$ctr."
\n";
+ $result.=(($ctr+1)%10 == 0 ? '
' : '');
+ $ctr++;
+ }
+ $result.='
';
+ $result.= '
or /'.
+ $weight{$partid}.' (problem weight)
'."\n";
+ $result.= '
'."\n";
+ $ctsparts++;
+ }
+ $result.='
'.'
'.'
'."\n".
+ '';
+ $result.='';
+
+ #table listing all the students in a section/class
+ #header of table
+ $result.= '
Assign Grade to Specific Students in '.$sectionClass;
+ $result.= '
'."\n".
+ '
No.
'.
+ '
'.&nameUserString('header')."
\n";
+ my (@parts) = sort(&getpartlist($url));
+ foreach my $part (@parts) {
+ my $display=&Apache::lonnet::metadata($url,$part.'.display');
+ $display =~ s|^Number of Attempts|Tries |; # makes the column narrower
+ if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
+ if ($display =~ /^Partial Credit Factor/) {
+ my ($partid) = &split_part_type($part);
+ $result.='
Score Part '.$partid.' (weight = '.
+ $weight{$partid}.')
';
+
+ #get info for each student
+ #list all the students - with points and grade status
+ my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'1');
+ my $ctr = 0;
+ foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
+ my $uname = $_;
+ $uname=~s/:/_/;
+ $result.=''."\n";
+ $ctr++;
+ $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},
+ $_,$$fullname{$_},\@parts,\%weight,$ctr);
+ }
+ $result.='
';
+ $result.=''."\n";
+ $result.=''."\n";
+ if (scalar(%$fullname) eq 0) {
+ my $colspan=3+scalar(@parts);
+ $result='There are no students in section "'.$ENV{'form.section'}.
+ '" with enrollment status "'.$ENV{'form.Status'}.'" to modify or grade.';
+ }
+ $result.=&show_grading_menu_form($symb,$url);
+ return $result;
}
-sub editgrades {
- my ($request) = @_;
- my $result='';
+#--- call by previous routine to display each student
+sub viewstudentgrade {
+ my ($url,$symb,$courseid,$student,$fullname,$parts,$weight,$ctr) = @_;
+ my ($uname,$udom) = split(/:/,$student);
+ $student=~s/:/_/;
+ my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
+ my $result='