--- loncom/homework/grades.pm 2003/07/21 13:32:49 1.121 +++ loncom/homework/grades.pm 2010/01/15 17:12:18 1.589 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.121 2003/07/21 13:32:49 ng Exp $ +# $Id: grades.pm,v 1.589 2010/01/15 17:12:18 bisitz Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,15 +25,8 @@ # # http://www.lon-capa.org/ # -# 2/9,2/13 Guy Albertelli -# 6/8 Gerd Kortemeyer -# 7/26 H.K. Ng -# 8/20 Gerd Kortemeyer -# Year 2002 -# June-August H.K. Ng -# Year 2003 -# February, March H.K. Ng -# + + package Apache::grades; use strict; @@ -44,116 +37,445 @@ use Apache::loncommon; use Apache::lonhtmlcommon; use Apache::lonnavmaps; use Apache::lonhomework; +use Apache::lonpickcode; use Apache::loncoursedata; -use Apache::lonmsg qw(:user_normal_msg); +use Apache::lonmsg(); use Apache::Constants qw(:common); +use Apache::lonlocal; +use Apache::lonenc; use String::Similarity; +use LONCAPA; + +use POSIX qw(floor); + + -my %oldessays=(); my %perm=(); -# ----- These first few routines are general use routines.---- +# These variables are used to recover from ssi errors + +my $ssi_retries = 5; +my $ssi_error; +my $ssi_error_resource; +my $ssi_error_message; + + +sub ssi_with_retries { + my ($resource, $retries, %form) = @_; + my ($content, $response) = &Apache::loncommon::ssi_with_retries($resource, $retries, %form); + if ($response->is_error) { + $ssi_error = 1; + $ssi_error_resource = $resource; + $ssi_error_message = $response->code . " " . $response->message; + } + + return $content; + +} # -# --- Retrieve the parts that matches stores_\d+ from the metadata file.--- +# Prodcuces an ssi retry failure error message to the user: +# + +sub ssi_print_error { + my ($r) = @_; + my $helpurl = &Apache::loncommon::top_nav_help('Helpdesk'); + $r->print(' +
+

'.&mt('An unrecoverable network error occurred:').'

+

+'.&mt('Unable to retrieve a resource from a server:').'
+'.&mt('Resource:').' '.$ssi_error_resource.'
+'.&mt('Error:').' '.$ssi_error_message.' +

+

'. +&mt('It is recommended that you try again later, as this error may mean the server was just temporarily unavailable, or is down for maintenance.').'
'. +&mt('If the error persists, please contact the [_1] for assistance.',$helpurl). +'

'); + return; +} + +# +# --- Retrieve the parts 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); + my ($symb,$errorref) = @_; + + my $navmap = Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + if (ref($errorref)) { + $$errorref = 'navmap'; + return; + } + } + my $res = $navmap->getBySymb($symb); + my $partlist = $res->parts(); + my $url = $res->src(); + my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys')); + + my @stores; + foreach my $part (@{ $partlist }) { + foreach my $key (@metakeys) { + if ($key =~ m/^stores_\Q$part\E_/) { push(@stores,$key); } } } - return @parts; + return @stores; } # --- 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 ($uname,$udom) = @_; - my %name=&Apache::lonnet::get('environment', ['lastname','generation', - 'firstname','middlename'], - $udom,$uname); - my $fullname; - my ($tmp) = keys(%name); - if ($tmp !~ /^(con_lost|error|no_such_host)/i) { - $fullname = &Apache::loncoursedata::ProcessFullName - (@name{qw/lastname generation firstname middlename/}); +sub get_symb { + my ($request,$silent) = @_; + (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 '') { + if (!$silent) { + $request->print("Unable to handle ambiguous references:$url:."); + return (); + } + } + &Apache::lonenc::check_decrypt(\$symb); + return ($symb); +} + +#--- 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 ' '.&mt('Fullname').' ('.&mt('Username').')'; } else { - &Apache::lonnet::logthis('grades.pm: no name data for '.$uname. - '@'.$udom.':'.$tmp); + return ' '.$fullname.' ('.$uname. + ($env{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')'; } - return $fullname; } #--- 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 $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!! - $handgrade{$part} = $responsetype.':'.($allkeys =~ /parameter_$part\_handgrade/ ? 'yes' : 'no'); - next if ($seen{$partid} > 0); - $seen{$partid}++; - push @partlist,$partid; - } + my ($symb,$response_error) = @_; + + my $navmap = Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + if (ref($response_error)) { + $$response_error = 1; + } + return; + } + my $res = $navmap->getBySymb($symb); + my $partlist = $res->parts(); + my %vPart = + map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart')); + my (%response_types,%handgrade); + foreach my $part (@{ $partlist }) { + next if (%vPart && !exists($vPart{$part})); + + my @types = $res->responseType($part); + my @ids = $res->responseIds($part); + for (my $i=0; $i < scalar(@ids); $i++) { + $response_types{$part}{$ids[$i]} = $types[$i]; + $handgrade{$part.'_'.$ids[$i]} = + &Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i]. + '.handgrade',$symb); + } + } + return ($partlist,\%handgrade,\%response_types); +} + +sub flatten_responseType { + my ($responseType) = @_; + my @part_response_id = + map { + my $part = $_; + map { + [$part,$_] + } sort(keys(%{ $responseType->{$part} })); + } sort(keys(%$responseType)); + return @part_response_id; +} + +sub get_display_part { + my ($partID,$symb)=@_; + my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb); + if (defined($display) and $display ne '') { + $display.= ' (' + .&mt('Part ID: [_1]',$partID).')'; + } else { + $display=$partID; } - return \@partlist,\%handgrade; + return $display; } #--- Show resource title #--- and parts and response type sub showResourceInfo { - my ($url,$probTitle) = @_; - my $result =''. - ''."\n"; - my ($partlist,$handgrade) = &response_type($url); - my ($resptype,$hdgrade)=('','no'); - for (sort keys(%$handgrade)) { - my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_}); - $resptype = $responsetype; - $hdgrade = $handgrade if ($handgrade eq 'yes'); - $result.=''. - ''; -# ''; + my ($symb,$probTitle,$checkboxes,$res_error) = @_; + my $result = '

'.&mt('Current Resource').': '.$probTitle.'

'."\n"; + my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error); + if (ref($res_error)) { + if ($$res_error) { + return; + } + } + $result.=&Apache::loncommon::start_data_table() + .&Apache::loncommon::start_data_table_header_row(); + if ($checkboxes) { + $result.=''; + } + $result.='' + .'' + .'' + .&Apache::loncommon::end_data_table_header_row(); + my %resptype = (); + my $hdgrade='no'; + my %partsseen; + foreach my $partID (sort(keys(%$responseType))) { + foreach my $resID (sort(keys(%{ $responseType->{$partID} }))) { + my $handgrade=$$handgrade{$partID.'_'.$resID}; + my $responsetype = $responseType->{$partID}->{$resID}; + $hdgrade = $handgrade if ($handgrade eq 'yes'); + $result.=&Apache::loncommon::start_data_table_row(); + if ($checkboxes) { + if (exists($partsseen{$partID})) { + $result.=""; + } else { + $result.=""; + } + $partsseen{$partID}=1; + } + my $display_part=&get_display_part($partID,$symb); + $result.='' + .'' + .'' +# .'' + .&Apache::loncommon::end_data_table_row(); + } + } + $result.=&Apache::loncommon::end_data_table(); + return $result,$responseType,$hdgrade,$partlist,$handgrade; +} + +sub reset_caches { + &reset_analyze_cache(); + &reset_perm(); +} + +{ + my %analyze_cache; + my %analyze_cache_formkeys; + + sub reset_analyze_cache { + undef(%analyze_cache); + undef(%analyze_cache_formkeys); + } + + sub get_analyze { + my ($symb,$uname,$udom,$no_increment,$add_to_hash)=@_; + my $key = "$symb\0$uname\0$udom"; + if (exists($analyze_cache{$key})) { + my $getupdate = 0; + if (ref($add_to_hash) eq 'HASH') { + foreach my $item (keys(%{$add_to_hash})) { + if (ref($analyze_cache_formkeys{$key}) eq 'HASH') { + if (!exists($analyze_cache_formkeys{$key}{$item})) { + $getupdate = 1; + last; + } + } else { + $getupdate = 1; + } + } + } + if (!$getupdate) { + return $analyze_cache{$key}; + } + } + + my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb); + $url=&Apache::lonnet::clutter($url); + my %form = ('grade_target' => 'analyze', + 'grade_domain' => $udom, + 'grade_symb' => $symb, + 'grade_courseid' => $env{'request.course.id'}, + 'grade_username' => $uname, + 'grade_noincrement' => $no_increment); + if (ref($add_to_hash)) { + %form = (%form,%{$add_to_hash}); + } + my $subresult=&ssi_with_retries($url, $ssi_retries,%form); + (undef,$subresult)=split(/_HASH_REF__/,$subresult,2); + my %analyze=&Apache::lonnet::str2hash($subresult); + if (ref($add_to_hash) eq 'HASH') { + $analyze_cache_formkeys{$key} = $add_to_hash; + } else { + $analyze_cache_formkeys{$key} = {}; + } + return $analyze_cache{$key} = \%analyze; + } + + sub get_order { + my ($partid,$respid,$symb,$uname,$udom,$no_increment)=@_; + my $analyze = &get_analyze($symb,$uname,$udom,$no_increment); + return $analyze->{"$partid.$respid.shown"}; + } + + sub get_radiobutton_correct_foil { + my ($partid,$respid,$symb,$uname,$udom)=@_; + my $analyze = &get_analyze($symb,$uname,$udom); + my $foils = &get_order($partid,$respid,$symb,$uname,$udom); + if (ref($foils) eq 'ARRAY') { + foreach my $foil (@{$foils}) { + if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') { + return $foil; + } + } + } + } + + sub scantron_partids_tograde { + my ($resource,$cid,$uname,$udom,$check_for_randomlist) = @_; + my (%analysis,@parts); + if (ref($resource)) { + my $symb = $resource->symb(); + my $add_to_form; + if ($check_for_randomlist) { + $add_to_form = { 'check_parts_withrandomlist' => 1,}; + } + my $analyze = &get_analyze($symb,$uname,$udom,undef,$add_to_form); + if (ref($analyze) eq 'HASH') { + %analysis = %{$analyze}; + } + if (ref($analysis{'parts'}) eq 'ARRAY') { + foreach my $part (@{$analysis{'parts'}}) { + my ($id,$respid) = split(/\./,$part); + if (!&Apache::loncommon::check_if_partid_hidden($id,$symb,$udom,$uname)) { + push(@parts,$part); + } + } + } + } + return (\%analysis,\@parts); } - $result.='
Current Resource: '.$probTitle.'
Part '.(split(/_/))[0].'Type: '.$responsetype.'
Handgrade: '.$handgrade.'
 '.&mt('Problem Part').''.&mt('Res. ID').''.&mt('Type').' '.$display_part.''.''.$resID.''.&mt($responsetype).''.&mt('Handgrade: [_1]',$handgrade).'
'."\n"; - return $result,$resptype,$hdgrade,$partlist,$handgrade; + } #--- Clean response type for display -#--- Currently filters option response type only. +#--- Currently filters option/rank/radiobutton/match/essay/Task +# response types only. sub cleanRecord { - my ($answer,$response) = @_; - 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).'
'; + my ($answer,$response,$symb,$partid,$respid,$record,$order,$version, + $uname,$udom) = @_; + my $grayFont = ''; + if ($response =~ /^(option|rank)$/) { + my %answer=&Apache::lonnet::str2hash($answer); + my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"}); + my ($toprow,$bottomrow); + foreach my $foil (@$order) { + if ($grading{$foil} == 1) { + $toprow.=''.$answer{$foil}.' '; + } else { + $toprow.=''.$answer{$foil}.' '; + } + $bottomrow.=''.$grayFont.$foil.' '; + } + return '
'. + ''.$toprow.''. + ''. + $grayFont.$bottomrow.''.'
'.&mt('Answer').'
'.$grayFont.&mt('Option ID').'
'; + } elsif ($response eq 'match') { + my %answer=&Apache::lonnet::str2hash($answer); + my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"}); + my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"}); + my ($toprow,$middlerow,$bottomrow); + foreach my $foil (@$order) { + my $item=shift(@items); + if ($grading{$foil} == 1) { + $toprow.=''.$item.' '; + $middlerow.=''.$grayFont.$answer{$foil}.' '; + } else { + $toprow.=''.$item.' '; + $middlerow.=''.$grayFont.$answer{$foil}.' '; + } + $bottomrow.=''.$grayFont.$foil.' '; + } + return '
'. + ''.$toprow.''. + ''. + $middlerow.''. + ''. + $bottomrow.''.'
'.&mt('Answer').'
'.$grayFont.&mt('Item ID').'
'.$grayFont.&mt('Option ID').'
'; + } elsif ($response eq 'radiobutton') { + my %answer=&Apache::lonnet::str2hash($answer); + my ($toprow,$bottomrow); + my $correct = + &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom); + foreach my $foil (@$order) { + if (exists($answer{$foil})) { + if ($foil eq $correct) { + $toprow.=''.&mt('true').''; + } else { + $toprow.=''.&mt('true').''; + } + } else { + $toprow.=''.&mt('false').''; + } + $bottomrow.=''.$grayFont.$foil.' '; + } + return '
'. + ''.$toprow.''. + ''. + $grayFont.$bottomrow.''.'
'.&mt('Answer').'
'.$grayFont.&mt('Option ID').'
'; + } elsif ($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. + } + $answer =~ s-\n-
-g; + return '

'.&keywords_highlight($answer).'
'; + } elsif ( $response eq 'organic') { + my $result='Smile representation: "'.$answer.'"'; + my $jme=$record->{$version."resource.$partid.$respid.molecule"}; + $result.=&Apache::chemresponse::jme_img($jme,$answer,400); + return $result; + } elsif ( $response eq 'Task') { + if ( $answer eq 'SUBMITTED') { + my $files = $record->{$version."resource.$respid.$partid.bridgetask.portfiles"}; + my $result = &Apache::bridgetask::file_list($files,$uname,$udom); + return $result; + } elsif ( grep(/^\Q$version\E.*?\.instance$/, keys(%{$record})) ) { + my @matches = grep(/^\Q$version\E.*?\.instance$/, + keys(%{$record})); + return join('
',($version,@matches)); + + + } else { + my $result = + '

' + .&mt('Overall result: [_1]', + $record->{$version."resource.$respid.$partid.status"}) + .'

'; + + $result .= ''; + return $result; + } + } elsif ( $response =~ m/(?:numerical|formula)/) { + $answer = + &Apache::loncommon::format_previous_attempt_value('submission', + $answer); } return $answer; } @@ -186,7 +508,8 @@ sub commonJSfunctions { } } } else { - if (selectOne.selected) return selectOne.value; + // only one value it must be the selected one + return selectOne.value; } } @@ -196,35 +519,86 @@ COMMONJSFUNCTIONS #--- Dumps the class list with usernames,list of sections, #--- section, ids and fullnames for each user. sub getclasslist { - my ($getsec,$filterlist) = @_; - $getsec = $getsec eq '' ? 'all' : $getsec; - my $classlist=&Apache::loncoursedata::get_classlist(); + my ($getsec,$filterlist,$getgroup) = @_; + my @getsec; + my @getgroup; + my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status')); + if (!ref($getsec)) { + if ($getsec ne '' && $getsec ne 'all') { + @getsec=($getsec); + } + } else { + @getsec=@{$getsec}; + } + if (grep(/^all$/,@getsec)) { undef(@getsec); } + if (!ref($getgroup)) { + if ($getgroup ne '' && $getgroup ne 'all') { + @getgroup=($getgroup); + } + } else { + @getgroup=@{$getgroup}; + } + if (grep(/^all$/,@getgroup)) { undef(@getgroup); } + + my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist(); # Bail out if we were unable to get the classlist return if (! defined($classlist)); + &Apache::loncoursedata::get_group_memberships($classlist,$keylist); # 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->{$_}}; + foreach my $student (keys(%$classlist)) { + my $end = + $classlist->{$student}->[&Apache::loncoursedata::CL_END()]; + my $start = + $classlist->{$student}->[&Apache::loncoursedata::CL_START()]; + my $id = + $classlist->{$student}->[&Apache::loncoursedata::CL_ID()]; + my $section = + $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()]; + my $fullname = + $classlist->{$student}->[&Apache::loncoursedata::CL_FULLNAME()]; + my $status = + $classlist->{$student}->[&Apache::loncoursedata::CL_STATUS()]; + my $group = + $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()]; # filter students according to status selected - if ($filterlist && $ENV{'form.Status'} ne 'Any') { - if ($ENV{'form.Status'} ne $status) { - delete ($classlist->{$_}); + if ($filterlist && (!($stu_status =~ /Any/))) { + if (!($stu_status =~ $status)) { + delete($classlist->{$student}); next; } } - $section = ($section ne '' ? $section : 'no'); + # filter students according to groups selected + my @stu_groups = split(/,/,$group); + if (@getgroup) { + my $exclude = 1; + foreach my $grp (@getgroup) { + foreach my $stu_group (@stu_groups) { + if ($stu_group eq $grp) { + $exclude = 0; + } + } + if (($grp eq 'none') && !$group) { + $exclude = 0; + } + } + if ($exclude) { + delete($classlist->{$student}); + } + } + $section = ($section ne '' ? $section : 'none'); if (&canview($section)) { - if ($getsec eq 'all' || $getsec eq $section) { + if (!@getsec || grep(/^\Q$section\E$/,@getsec)) { $sections{$section}++; - $fullnames{$_}=$fullname; + if ($classlist->{$student}) { + $fullnames{$student}=$fullname; + } } else { - delete($classlist->{$_}); + delete($classlist->{$student}); } } else { - delete($classlist->{$_}); + delete($classlist->{$student}); } } my %seen = (); @@ -274,11 +648,11 @@ sub canview { #--- Retrieve the grade status of a student for all the parts sub student_gradeStatus { - my ($url,$symb,$udom,$uname,$partlist) = @_; - my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname); + my ($symb,$udom,$uname,$partlist) = @_; + 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"; @@ -291,7 +665,8 @@ sub student_gradeStatus { # Use by verifyscript and viewgrades # Shows a student's view of problem and submission sub jscriptNform { - my ($url,$symb) = @_; + my ($symb) = @_; + my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status')); my $jscript=''."\n"; $jscript.= '
'."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". + ''."\n". + ''."\n". + ''."\n". + ''."\n". ''."\n". ''."\n". ''."\n". @@ -311,6 +686,35 @@ sub jscriptNform { return $jscript; } + + +# Given the score (as a number [0-1] and the weight) what is the final +# point value? This function will round to the nearest tenth, third, +# or quarter if one of those is within the tolerance of .00001. +sub compute_points { + my ($score, $weight) = @_; + + my $tolerance = .00001; + my $points = $score * $weight; + + # Check for nearness to 1/x. + my $check_for_nearness = sub { + my ($factor) = @_; + my $num = ($points * $factor) + $tolerance; + my $floored_num = floor($num); + if ($num - $floored_num < 2 * $tolerance * $factor) { + return $floored_num / $factor; + } + return $points; + }; + + $points = $check_for_nearness->(10); + $points = $check_for_nearness->(3); + $points = $check_for_nearness->(4); + + return $points; +} + #------------------ End of general use routines -------------------- # @@ -318,12 +722,16 @@ sub jscriptNform { # sub most_similar { - my ($uname,$udom,$uessay)=@_; + my ($uname,$udom,$uessay,$old_essays)=@_; # ignore spaces and punctuation $uessay=~s/\W+/ /gs; +# ignore empty submissions (occuring when only files are sent) + + unless ($uessay=~/\w+/) { return ''; } + # these will be returned. Do not care if not at least 50 percent similar my $limit=0.6; my $sname=''; @@ -331,23 +739,22 @@ sub most_similar { my $scrsid=''; my $sessay=''; # go through all essays ... - foreach my $tkey (keys %oldessays) { - my ($tname,$tdom,$tcrsid)=split(/\./,$tkey); + foreach my $tkey (keys(%$old_essays)) { + my ($tname,$tdom,$tcrsid)=map {&unescape($_)} (split(/\./,$tkey)); # ... except the same student - if (($tname ne $uname) || ($tdom ne $udom)) { - my $tessay=$oldessays{$tkey}; - $tessay=~s/\W+/ /gs; + next if (($tname eq $uname) && ($tdom eq $udom)); + my $tessay=$old_essays->{$tkey}; + $tessay=~s/\W+/ /gs; # String similarity gives up if not even limit - my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$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 ($tsimilar>$limit) { + $limit=$tsimilar; + $sname=$tname; + $sdom=$tdom; + $scrsid=$tcrsid; + $sessay=$old_essays->{$tkey}; + } } if ($limit>0.6) { return ($sname,$sdom,$scrsid,$sessay,$limit); @@ -364,51 +771,88 @@ sub most_similar { sub verifyreceipt { my $request = shift; - my $courseid = $ENV{'request.course.id'}; - my $receipt = unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'. - $ENV{'form.receipt'}; + my $courseid = $env{'request.course.id'}; + my $receipt = &Apache::lonnet::recprefix($courseid).'-'. + $env{'form.receipt'}; $receipt =~ s/[^\-\d]//g; - my $url = $ENV{'form.url'}; - my $symb = $ENV{'form.symb'}; - unless ($symb) { - $symb = &Apache::lonnet::symbread($url); - } + my ($symb) = &get_symb($request); - my $title.='

Verifying Submission Receipt '. - $receipt.'

'."\n". - 'Resource: '.$ENV{'form.probTitle'}.'

'."\n"; + my $title.= + '

'. + &mt('Verifying Receipt No. [_1]',$receipt). + '

'."\n". + '

'.&mt('Resource: [_1]',$env{'form.probTitle'}). + '

'."\n"; my ($string,$contents,$matches) = ('','',0); my (undef,undef,$fullname) = &getclasslist('all','0'); - - foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { + + my $receiptparts=0; + if ($env{"course.$courseid.receiptalg"} eq 'receipt2' || + $env{"course.$courseid.receiptalg"} eq 'receipt3') { $receiptparts=1; } + my $parts=['0']; + if ($receiptparts) { + my $res_error; + ($parts)=&response_type($symb,\$res_error); + if ($res_error) { + return &navmap_errormsg(); + } + } + + my $header = + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(). + ' '.&mt('Fullname').' '."\n". + ' '.&mt('Username').' '."\n". + ' '.&mt('Domain').' '; + if ($receiptparts) { + $header.=' '.&mt('Problem Part').' '; + } + $header.= + &Apache::loncommon::end_data_table_header_row(); + + foreach (sort + { + if (lc($$fullname{$a}) ne lc($$fullname{$b})) { + return (lc($$fullname{$a}) cmp lc($$fullname{$b})); + } + return $a cmp $b; + } (keys(%$fullname))) { my ($uname,$udom)=split(/\:/); - if ($receipt eq - &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) { - $contents.=' '."\n". - ''.$$fullname{$_}.' '."\n". - ' '.$uname.' '. - ' '.$udom.' '."\n"; - - $matches++; + foreach my $part (@$parts) { + if ($receipt eq &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb,$part)) { + $contents.= + &Apache::loncommon::start_data_table_row(). + ' '."\n". + ''.$$fullname{$_}.' '."\n". + ' '.$uname.' '. + ' '.$udom.' '; + if ($receiptparts) { + $contents.=' '.$part.' '; + } + $contents.= + &Apache::loncommon::end_data_table_row()."\n"; + + $matches++; + } } } if ($matches == 0) { - $string = $title.'No match found for the above receipt.'; + $string = $title + .'

' + .&mt('No match found for the above receipt number.') + .'

'; } else { - $string = &jscriptNform($url,$symb).$title. - 'The above receipt matches the following student'. - ($matches <= 1 ? '.' : 's.')."\n". - '
'."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". + $string = &jscriptNform($symb).$title. + '

'. + &mt('The above receipt number matches the following [quant,_1,student].',$matches). + '

'. + $header. $contents. - '
 Fullname  Username  Domain 
'."\n"; + &Apache::loncommon::end_data_table()."\n"; } - return $string.&show_grading_menu_form($symb,$url); + return $string.&show_grading_menu_form($symb); } #--- This is called by a number of programs. @@ -418,22 +862,26 @@ sub verifyreceipt { 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,$resptype,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$ENV{'form.probTitle'}); - $result.=$table; - + my ($symb) = &get_symb($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 $getgroup = $env{'form.group'} eq '' ? 'all' : $env{'form.group'}; + 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='

 ' + .&mt("$viewgrade Submissions for a Student or a Group of Students") + .'

'; + + my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes')); + + my %lt = &Apache::lonlocal::texthash ( + 'multiple' => 'Please select a student or group of students before clicking on the Next button.', + 'single' => 'Please select the student before clicking on the Next button.', + ); $request->print(< function checkSelect(checkBox) { @@ -445,15 +893,15 @@ sub listStudents { ctr++; } } - sense = "a student or group of students"; + sense = '$lt{'multiple'}'; } else { if (checkBox.checked) { ctr = 1; } - sense = "the student"; + sense = '$lt{'single'}'; } if (ctr == 0) { - alert("Please select "+sense+" before clicking on the $viewgrade button."); + alert(sense); return false; } document.gradesub.submit(); @@ -470,143 +918,283 @@ 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". - ' View Problem Text: no '."\n". - ' one student '."\n". - ' all students
'."\n". - ' Submissions: '."\n"; - if ($ENV{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) { - $gradeTable.=' essay part only'."\n"; - } - - my $saveStatus = $ENV{'form.Status'} eq '' ? 'Active' : $ENV{'form.Status'}; - $ENV{'form.Status'} = $saveStatus; - - $gradeTable.=' last sub only'."\n". - ' last sub & parts info'."\n". - ' all details'."\n". - ''."\n". + my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked="checked"' : ''; + my $checklastsub = $checkhdgrade eq '' ? 'checked="checked"' : ''; + my $gradeTable=''. + "\n".$table; + + $gradeTable .= &Apache::lonhtmlcommon::start_pick_box(); + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text')) + .''."\n" + .''."\n" + .'
'."\n" + .&Apache::lonhtmlcommon::row_closure(); + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer')) + .''."\n" + .''."\n" + .'
'."\n" + .&Apache::lonhtmlcommon::row_closure(); + + my $submission_options; + if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) { + $submission_options.= + ''."\n"; + } + my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status')); + my $saveStatus = $stu_status eq '' ? 'Active' : $stu_status; + $env{'form.Status'} = $saveStatus; + $submission_options.= + ''."\n". + ''."\n". + ''."\n". + ''; + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Submissions')) + .$submission_options + .&Apache::lonhtmlcommon::row_closure(); + + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments')) + .'' + .&Apache::lonhtmlcommon::row_closure(); + + $gradeTable .= + &build_section_inputs(). ''."\n". - ''."\n". - '
'."\n". - '
'."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". + '
'."\n". + '
'."\n". + ''."\n". + ''."\n". + ''."\n". ''."\n"; - $gradeTable.='Student Status: '. - &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);').'
'; + if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) { + $gradeTable .= ''."\n"; + } else { + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status')) + .&Apache::lonhtmlcommon::StatusOptions( + $saveStatus,undef,1,'javascript:reLoadList(this.form);') + .&Apache::lonhtmlcommon::row_closure(); + } + + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism')) + .'' + .&Apache::lonhtmlcommon::row_closure(1) + .&Apache::lonhtmlcommon::end_pick_box(); + + $gradeTable .= '

' + .&mt('To '.lc($viewgrade)." a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.")."\n" + .'' + .'

'; - $gradeTable.='To '.lc($viewgrade).' a submission, click on the check box next to the student\'s name. Then '."\n". - 'click on the '.$viewgrade.' button. To view the submissions for a group of students, click'."\n". - ' on the check boxes for the group of students.
'."\n". - ''."\n"; +# checkall buttons + $gradeTable.=&check_script('gradesub', 'stuinfo'); $gradeTable.=''."\n"; - - my (undef, undef, $fullname) = &getclasslist($getsec,'1'); - $gradeTable.='
'. - ''; + 'onclick="javascript:checkSelect(this.form.stuinfo);" '."\n". + 'value="'.&mt('Next').' →" />
'."\n"; + $gradeTable.=&check_buttons(); + my ($classlist, undef, $fullname) = &getclasslist($getsec,'1',$getgroup); + $gradeTable.= &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(); my $loop = 0; while ($loop < 2) { - $gradeTable.=''; - if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { - foreach (sort(@$partlist)) { - $gradeTable.=''; + $gradeTable.=''. + ''; + if ($env{'form.showgrading'} eq 'yes' + && $submitonly ne 'queued' + && $submitonly ne 'all') { + foreach my $part (sort(@$partlist)) { + my $display_part= + &get_display_part((split(/_/,$part))[0],$symb); + $gradeTable.= + ''; } + } elsif ($submitonly eq 'queued') { + $gradeTable.=''; } $loop++; +# $gradeTable.='' if ($loop%2 ==1); } - $gradeTable.=''."\n"; + $gradeTable.=&Apache::loncommon::end_data_table_header_row()."\n"; my $ctr = 0; - foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { + foreach my $student (sort + { + if (lc($$fullname{$a}) ne lc($$fullname{$b})) { + return (lc($$fullname{$a}) cmp lc($$fullname{$b})); + } + return $a cmp $b; + } + (keys(%$fullname))) { my ($uname,$udom) = split(/:/,$student); + my %status = (); - if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { - (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist); - my $statusflg = ''; + + if ($submitonly eq 'queued') { + my %queue_status = + &Apache::bridgetask::get_student_status($symb,$cdom,$cnum, + $udom,$uname); + next if (!defined($queue_status{'gradingqueue'})); + $status{'gradingqueue'} = $queue_status{'gradingqueue'}; + } + + if ($env{'form.showgrading'} eq 'yes' + && $submitonly ne 'queued' + && $submitonly ne 'all') { + (%status) =&student_gradeStatus($symb,$udom,$uname,$partlist); + my $submitted = 0; + my $graded = 0; + my $incorrect = 0; foreach (keys(%status)) { - $statusflg = 1 if ($status{$_} ne 'nothing'); + $submitted = 1 if ($status{$_} ne 'nothing'); + $graded = 1 if ($status{$_} =~ /^ungraded/); + $incorrect = 1 if ($status{$_} =~ /^incorrect/); + my ($foo,$partid,$foo1) = split(/\./,$_); if ($status{'resource.'.$partid.'.submitted_by'} ne '') { - $statusflg = ''; + $submitted = 0; + my ($part)=split(/\./,$partid); $gradeTable.=''; } } - next if ($statusflg eq '' && $submitonly eq 'yes'); + + next if (!$submitted && ($submitonly eq 'yes' || + $submitonly eq 'incorrect' || + $submitonly eq 'graded')); + next if (!$graded && ($submitonly eq 'graded')); + next if (!$incorrect && $submitonly eq 'incorrect'); } $ctr++; + my $section = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()]; + my $group = $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()]; if ( $perm{'vgr'} eq 'F' ) { - $gradeTable.='' if ($ctr%2 ==1); - $gradeTable.=''."\n". - ''."\n"; - - if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { - foreach (sort keys(%status)) { - next if (/^resource.*?submitted_by$/); - $gradeTable.=''."\n"; + if ($ctr%2 ==1) { + $gradeTable.= &Apache::loncommon::start_data_table_row(); + } + $gradeTable.=''. + ''."\n".''."\n"; + + if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { + foreach (sort(keys(%status))) { + next if ($_ =~ /^resource.*?submitted_by$/); + $gradeTable.=''."\n"; } } - $gradeTable.=''."\n" if ($ctr%2 ==0); +# $gradeTable.='' if ($ctr%2 ==1); + if ($ctr%2 ==0) { + $gradeTable.=&Apache::loncommon::end_data_table_row()."\n"; + } } } if ($ctr%2 ==1) { - $gradeTable.=''; - if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { + $gradeTable.=''; + if ($env{'form.showgrading'} eq 'yes' + && $submitonly ne 'queued' + && $submitonly ne 'all') { foreach (@$partlist) { $gradeTable.=''; } + } elsif ($submitonly eq 'queued') { + $gradeTable.=''; } - $gradeTable.=''; + $gradeTable.=&Apache::loncommon::end_data_table_row(); } - $gradeTable.='
 Select  Fullname '. - '(Username)  Part '.(split(/_/))[0].' Status '.&mt('No.').''.&mt('Select').''.&nameUserString('header').' '.&mt('Section/Group').''.&mt('Part: [_1] Status',$display_part).''.&mt('Queue Status').' 
 '.$$fullname{$student}.' '."\n". - '('.$uname.') '.$status{$_}.' '.$ctr.' '. + &nameUserString(undef,$$fullname{$student},$uname,$udom). + ' '.$section.($group ne '' ?'/'.$group:'').' '.&mt($status{$_}).' 
       
'. - ''."\n"; + $gradeTable.=&Apache::loncommon::end_data_table()."\n". + ''."\n"; if ($ctr == 0) { my $num_students=(scalar(keys(%$fullname))); if ($num_students eq 0) { - $gradeTable='
 There are no students currently enrolled.'; + $gradeTable='
 '.&mt('There are no students currently enrolled.').''; } else { - $gradeTable='
 '. - 'No submissions found for this resource for any students. ('.$num_students. - ' checked for submissions
'; + my $submissions='submissions'; + if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; } + if ($submitonly eq 'graded' ) { $submissions = 'ungraded submissions'; } + if ($submitonly eq 'queued' ) { $submissions = 'queued submissions'; } + $gradeTable='
 '. + &mt('No '.$submissions.' found for this resource for any students. ([_1] students checked for '.$submissions.')', + $num_students). + '
'; } } elsif ($ctr == 1) { - $gradeTable =~ s/type=checkbox/type=checkbox checked/; + $gradeTable =~ s/type="checkbox"/type="checkbox" checked="checked"/; } - $gradeTable.=&show_grading_menu_form($symb,$url); + $gradeTable.=&show_grading_menu_form($symb); $request->print($gradeTable); return ''; } #---- Called from the listStudents routine + +sub check_script { + my ($form, $type)=@_; + my $chkallscript=''."\n"; + return $chkallscript; +} + +sub check_buttons { + my $buttons.=''; + $buttons.=' '; + $buttons.=''; + $buttons.=' '; + return $buttons; +} + # 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 @stuchecked = &Apache::loncommon::get_env_multiple('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; + foreach my $student (@stuchecked) { + my ($uname,$udom,$fullname) = split(/:/,$student); + $env{'form.student'} = $uname; + $env{'form.userdom'} = $udom; + $env{'form.fullname'} = $fullname; &submission($request,$ctr,$total); $ctr++; } @@ -621,17 +1209,18 @@ sub processGroup { #--- Javascript to handle the submission page functionality --- sub sub_page_js { my $request = shift; + my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = '); $request->print(< function updateRadio(formname,id,weight) { - var gradeBox = eval("formname.GD_BOX"+id); - var radioButton = eval("formname.RADVAL"+id); - var oldpts = eval("formname.oldpts"+id+".value"); + 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); + alert("$alertmsg"+pts); for (var i=0; idir_config('lonIconsURL'); &commonJSfunctions($request); + + my $inner_js_msg_central=< + function checkInput() { + opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value); + var nmsg = opener.document.SCORE.savemsgN.value; + var usrctr = document.msgcenter.usrctr.value; + var newval = opener.document.SCORE["newmsg"+usrctr]; + newval.value = opener.checkEntities(document.msgcenter.newmsg.value); + + var msgchk = ""; + if (document.msgcenter.subchk.checked) { + msgchk = "msgsub,"; + } + var includemsg = 0; + for (var i=1; i<=nmsg; i++) { + var opnmsg = opener.document.SCORE["savemsg"+i]; + var frmmsg = document.msgcenter["msg"+i]; + opnmsg.value = opener.checkEntities(frmmsg.value); + var showflg = opener.document.SCORE["shownOnce"+i]; + showflg.value = "1"; + var chkbox = document.msgcenter["msgn"+i]; + if (chkbox.checked) { + msgchk += "savemsg"+i+","; + includemsg = 1; + } + } + if (document.msgcenter.newmsgchk.checked) { + msgchk += "newmsg"+usrctr; + includemsg = 1; + } + imgformname = opener.document.SCORE["mailicon"+usrctr]; + imgformname.src = "$iconpath/"+((includemsg) ? "mailto.gif" : "mailbkgrd.gif"); + var includemsg = opener.document.SCORE["includemsg"+usrctr]; + includemsg.value = msgchk; + + self.close() + + } + +INNERJS + + my $inner_js_highlight_central=< + function updateChoice(flag) { + opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr); + opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize); + opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle); + opener.document.SCORE.refresh.value = "on"; + if (opener.document.SCORE.keywords.value!=""){ + opener.document.SCORE.submit(); + } + self.close() + } + +INNERJS + + my $start_page_msg_central = + &Apache::loncommon::start_page('Message Central',$inner_js_msg_central, + {'js_ready' => 1, + 'only_body' => 1, + 'bgcolor' =>'#FFFFFF',}); + my $end_page_msg_central = + &Apache::loncommon::end_page({'js_ready' => 1}); + + + my $start_page_highlight_central = + &Apache::loncommon::start_page('Highlight Central', + $inner_js_highlight_central, + {'js_ready' => 1, + 'only_body' => 1, + 'bgcolor' =>'#FFFFFF',}); + my $end_page_highlight_central = + &Apache::loncommon::end_page({'js_ready' => 1}); + + my $docopen=&Apache::lonhtmlcommon::javascript_docopen(); + $docopen=~s/^document\.//; + my $alertmsg = &mt('Please select a word or group of words from document and then click this link.'); $request->print(< //===================== Show list of keywords ==================== - function keywords(keyform) { - var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",keyform.value); + 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; - keyform.value = nret; + formname.keywords.value = nret; - document.SCORE.refresh.value = "on"; - if (document.SCORE.keywords.value != "") { - document.SCORE.submit(); + if (formname.keywords.value != "") { + formname.refresh.value = "on"; + formname.submit(); } return; } @@ -834,15 +1498,14 @@ sub sub_page_kw_js { else return; var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," "); if (cleantxt=="") { - alert("Please select a word or group of words from document and then click this link."); + alert("$alertmsg"); return; } 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.refresh.value = "on"; + document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret; if (document.SCORE.keywords.value != "") { + document.SCORE.refresh.value = "on"; document.SCORE.submit(); } return; @@ -859,21 +1522,23 @@ sub sub_page_kw_js { var Nmsg = msgform.savemsgN.value; savedMsgHeader(Nmsg,usrctr,fullname); var subject = msgform.msgsub.value; - var rtrchk = eval("document.SCORE.includemsg"+usrctr); - var msgchk = rtrchk.value; + var msgchk = document.SCORE["includemsg"+usrctr].value; re = /msgsub/; var shwsel = ""; if (re.test(msgchk)) { shwsel = "checked" } - displaySubject(subject,shwsel); + subject = (document.SCORE.shownSub.value == 0 ? checkEntities(subject) : subject); + displaySubject(checkEntities(subject),shwsel); for (var i=1; i<=Nmsg; i++) { - var testpt = "savemsg"+i+","; - re = /testpt/; + var testmsg = "savemsg"+i+","; + re = new RegExp(testmsg,"g"); shwsel = ""; if (re.test(msgchk)) { shwsel = "checked" } - var message = eval("document.SCORE.savemsg"+i+".value"); - displaySavedMsg(i,message,shwsel); + 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 = document.SCORE["newmsg"+usrctr].value; shwsel = ""; re = /newmsg/; if (re.test(msgchk)) { shwsel = "checked" } @@ -882,6 +1547,22 @@ sub sub_page_kw_js { return; } + 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"; @@ -889,99 +1570,58 @@ sub sub_page_kw_js { height = 600; scrollbar = "yes"; } -// if (window.pWin) {window.pWin.close(); window.pWin=null} var xpos = (screen.width-600)/2; xpos = (xpos < 0) ? '0' : xpos; var ypos = (screen.height-height)/2-30; ypos = (ypos < 0) ? '0' : ypos; - pWin = window.open('', 'MessageCenter', 'toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height); + pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height); pWin.focus(); pDoc = pWin.document; - pDoc.write(""); - pDoc.write("Message Central"); - - pDoc.write(" SUBJAVASCRIPT } +sub get_increment { + my $increment = $env{'form.increment'}; + if ($increment != 1 && $increment != .5 && $increment != .25 && + $increment != .1) { + $increment = 1; + } + return $increment; +} + +sub gradeBox_start { + return ( + &Apache::loncommon::start_data_table() + .&Apache::loncommon::start_data_table_header_row() + .''.&mt('Part').'' + .''.&mt('Points').'' + .' ' + .''.&mt('Assign Grade').'' + .''.&mt('Weight').'' + .''.&mt('Grade Status').'' + .&Apache::loncommon::end_data_table_header_row() + ); +} + +sub gradeBox_end { + return ( + &Apache::loncommon::end_data_table() + ); +} #--- 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 $checkIcon = ''.&mt('Check Mark').
+	''; my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname); - my $wgtmsg = ($wgt > 0 ? '(problem weight)' : - 'problem weight assigned by computer'); + my $wgtmsg = ($wgt > 0) ? &mt('(problem weight)') + : ''.&mt('problem weight assigned by computer').''; $wgt = ($wgt > 0 ? $wgt : '1'); my $score = ($$record{'resource.'.$partid.'.awarded'} eq '' ? - '' : $$record{'resource.'.$partid.'.awarded'}*$wgt); + '' : &compute_points($$record{'resource.'.$partid.'.awarded'},$wgt)); my $result=''."\n"; - - $result.='
'. - 'Part '.$partid.' Points: '."\n"; - + my $display_part= &get_display_part($partid,$symb); + my %last_resets = &get_last_resets($symb,$env{'request.course.id'}, + [$partid]); + my $aggtries = $$record{'resource.'.$partid.'.tries'}; + if ($last_resets{$partid}) { + $aggtries = &get_num_tries($record,$last_resets{$partid},$partid); + } + $result.=&Apache::loncommon::start_data_table_row(); my $ctr = 0; - $result.=''."\n"; # display radio buttons in a nice table 10 across - while ($ctr<=$wgt) { - $result.= '\n"; - $result.=(($ctr+1)%10 == 0 ? '' : ''); + my $thisweight = 0; + my $increment = &get_increment(); + + my $radio.='
'.$ctr."
'."\n"; # display radio buttons in a nice table 10 across + while ($thisweight<=$wgt) { + $radio.= '\n"; + $radio.=(($ctr+1)%10 == 0 ? '' : ''); + $thisweight += $increment; $ctr++; } - $result.='
'; + $radio.='
'; - $result.=' or '."\n"; - $result.=''."\n"; - $result.='/'.$wgt.' '.$wgtmsg. + $line.='/'.$wgt.' '.$wgtmsg. ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? ' '.$checkIcon : ''). - ' '."\n"; - - $result.=''."\n"; if ($$record{'resource.'.$partid.'.solved'} eq 'excused') { - $result.=''. - ''."\n"; + $line.=''. + ''; } else { - $result.=''. - ''."\n"; + $line.=''. + ''; } - $result.="  \n"; + $line.=''."\n"; + + + #&mt('Part:[_1]Points:[_2]or[_3]',$display_part,$radio,$line); + $result .= + ''.$display_part.''.$radio.''.&mt('or').''.$line.''; + $result.=&Apache::loncommon::end_data_table_row(); $result.=''."\n". ''."\n". ''."\n"; - $result.=''."\n"; + $$record{'resource.'.$partid.'.solved'}.'" />'."\n". + ''."\n". + ''."\n"; + my $res_error; + $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record,\$res_error); + if ($res_error) { + return &navmap_errormsg(); + } return $result; } +sub handback_box { + my ($symb,$uname,$udom,$counter,$partid,$record,$res_error) = @_; + my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error); + my (@respids); + my @part_response_id = &flatten_responseType($responseType); + foreach my $part_response_id (@part_response_id) { + my ($part,$resp) = @{ $part_response_id }; + if ($part eq $partid) { + push(@respids,$resp); + } + } + my $result; + foreach my $respid (@respids) { + my $prefix = $counter.'_'.$partid.'_'.$respid.'_'; + my $files=&get_submitted_files($udom,$uname,$partid,$respid,$record); + next if (!@$files); + my $file_counter = 1; + foreach my $file (@$files) { + if ($file =~ /\/portfolio\//) { + my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|); + my ($name,$version,$ext) = &file_name_version_ext($file_disp); + $file_disp = "$name.$ext"; + $file = $file_path.$file_disp; + $result.=&mt('Return commented version of [_1] to student.', + ''.$file_disp.''); + $result.=''."\n"; + $result.='
'; + $result.='('.&mt('File will be uploaded when you click on Save & Next below.').')
'; + $file_counter++; + } + } + } + return $result; +} + sub show_problem { - my ($request,$symb,$uname,$udom,$removeform,$viewon) = @_; - my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom, - $ENV{'request.course.id'}); + my ($request,$symb,$uname,$udom,$removeform,$viewon,$mode,$form) = @_; + my $rendered; + my %form = ((ref($form) eq 'HASH')? %{$form} : ()); + &Apache::lonxml::remember_problem_counter(); + if ($mode eq 'both' or $mode eq 'text') { + $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom, + $env{'request.course.id'}, + undef,\%form); + } if ($removeform) { $rendered=~s|||g; $rendered=~s|||g; - $rendered=~s|name="submit"|name="would_have_been_submit"|g; + $rendered=~s|(]*name\s*=\s*"?)(\w+)("?)|$1would_have_been_$2$3|g; + } + my $companswer; + if ($mode eq 'both' or $mode eq 'answer') { + &Apache::lonxml::restore_problem_counter(); + $companswer= + &Apache::loncommon::get_student_answers($symb,$uname,$udom, + $env{'request.course.id'}, + %form); } - my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom, - $ENV{'request.course.id'}); if ($removeform) { $companswer=~s|||g; $companswer=~s|||g; - $rendered=~s|name="submit"|name="would_have_been_submit"|g; + $companswer=~s|name="submit"|name="would_have_been_submit"|g; + } + $rendered= + '
' + .'

'.&mt('View of the problem').'

' + .$rendered + .'
'; + $companswer= + '
' + .'

'.&mt('Correct answer').'

' + .$companswer + .'
'; + my $result; + if ($mode eq 'both') { + $result=$rendered.$companswer; + } elsif ($mode eq 'text') { + $result=$rendered; + } elsif ($mode eq 'answer') { + $result=$companswer; } - my $result.='
'; - $result.=''; - $result.='' if ($viewon); - $result.='
View of the problem - '.$ENV{'form.fullname'}. - '
'.$rendered.'
'; - $result.='Correct answer:
'.$companswer; - $result.='
'; - $result.='

'; return $result; } +sub files_exist { + my ($r, $symb) = @_; + my @students = &Apache::loncommon::get_env_multiple('form.stuinfo'); + + foreach my $student (@students) { + my ($uname,$udom,$fullname) = split(/:/,$student); + my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'}, + $udom,$uname); + my ($string,$timestamp)= &get_last_submission(\%record); + foreach my $submission (@$string) { + my ($partid,$respid) = + ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/); + my $files=&get_submitted_files($udom,$uname,$partid,$respid, + \%record); + return 1 if (@$files); + } + } + return 0; +} + +sub download_all_link { + my ($r,$symb) = @_; + my $all_students = + join("\n", &Apache::loncommon::get_env_multiple('form.stuinfo')); + + my $parts = + join("\n",&Apache::loncommon::get_env_multiple('form.vPart')); + + my $identifier = &Apache::loncommon::get_cgi_id(); + &Apache::lonnet::appenv({'cgi.'.$identifier.'.students' => $all_students, + 'cgi.'.$identifier.'.symb' => $symb, + 'cgi.'.$identifier.'.parts' => $parts,}); + $r->print(''. + &mt('Download All Submitted Documents').''); + return +} + +sub build_section_inputs { + my $section_inputs; + if ($env{'form.section'} eq '') { + $section_inputs .= ''."\n"; + } else { + my @sections = &Apache::loncommon::get_env_multiple('form.section'); + foreach my $section (@sections) { + $section_inputs .= ''."\n"; + } + } + return $section_inputs; +} + # --------------------------- 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 ''; } + 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'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq ''; + my $symb = &get_symb($request); + if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); 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)); + $request->print('Unable to view requested student.('. + $uname.':'.$udom.' in section '.$usec.' in course id '. + $env{'request.course.id'}.')'); + $request->print(&show_grading_menu_form($symb)); return; } - $ENV{'form.lastSub'} = ($ENV{'form.lastSub'} eq '' ? 'all' : $ENV{'form.lastSub'}); - my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : ''); + if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; } + if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; } + if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; } + my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : ''); + my $checkIcon = ''.&mt('Check Mark').
+	''; + my %old_essays; # 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 $checkIcon = ''; - my $checkMark='

 Note: Part(s) graded correct by the computer is marked with a '. - $checkIcon.' symbol.'."\n"; - $request->print($checkMark); + &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'}; + if ($env{'form.handgrade'} eq 'yes' && &files_exist($request, $symb)) { + &download_all_link($request, $symb); } + $request->print('

 '.&mt('Submission Record').'

'."\n". + '

 '.&mt('Resource: [_1]',$env{'form.probTitle'}).'

'."\n"); # 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)); + if ($env{'form.vProb'} eq 'yes' or $env{'form.vAns'} eq 'yes') { + my $mode; + if ($env{'form.vProb'} eq 'yes' && $env{'form.vAns'} eq 'yes') { + $mode='both'; + } elsif ($env{'form.vProb'} eq 'yes') { + $mode='text'; + } elsif ($env{'form.vAns'} eq 'yes') { + $mode='answer'; + } + &Apache::lonxml::clear_problem_counter(); + $request->print(&show_problem($request,$symb,$uname,$udom,0,1,$mode)); } - + # 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') { + 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'; + $env{'course.'.$env{'request.course.id'}.'.domain'}, + $env{'course.'.$env{'request.course.id'}.'.num'}); - } - my $overRideScore = $ENV{'form.overRideScore'} eq '' ? 'no' : $ENV{'form.overRideScore'}; - - $request->print('
'."\n". + 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'}; + my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status')); + $request->print(''."\n". ''."\n". - ''."\n". - ''."\n". + ''."\n". + ''."\n". ''."\n". - ''."\n". + ''."\n". ''."\n". ''."\n". ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". + ''."\n". + ''."\n". + ''."\n". + ''."\n". + ''."\n". + &build_section_inputs(). + ''."\n". + ''."\n". ''."\n"); + ' value="'.($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : $total+1).'" />'."\n"); + if ($env{'form.handgrade'} eq 'yes') { + $request->print(''."\n". + ''."\n". + ''."\n". + ''."\n". + ''."\n". + ''."\n". + ''."\n"); + foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) { + $request->print(''."\n"); + } + } my ($cts,$prnmsg) = (1,''); - while ($cts <= $ENV{'form.savemsgN'}) { + while ($cts <= $env{'form.savemsgN'}) { $prnmsg.=''."\n"; + '" />'."\n". + ''."\n"; $cts++; } $request->print($prnmsg); - if ($ENV{'form.handgrade'} eq 'yes' && $ENV{'form.showgrading'} eq 'yes') { + if ($env{'form.handgrade'} eq 'yes' && $env{'form.showgrading'} eq 'yes') { # # Print out the keyword options line # $request->print(<Keyword Options:  -List    -List    +Paste Selection to List    -Highlight Attribute

+Highlight Attribute

KEYWORDS # # Load the other essays for similarity check # - my $essayurl=&Apache::lonnet::declutter($url); - my ($adom,$aname,$apath)=($essayurl=~/^(\w+)\/(\w+)\/(.*)$/); - $apath=&Apache::lonnet::escape($apath); + my (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb); + my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/); + $apath=&escape($apath); $apath=~s/\W/\_/gs; - %oldessays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname); + %old_essays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname); } } - if ($ENV{'form.vProb'} eq 'all') { - $request->print('


') if ($counter > 0); - $request->print(&show_problem($request,$symb,$uname,$udom,1,1)); +# This is where output for one specific student would start + my $add_class = ($counter%2) ? 'LC_grade_show_user_odd_row' : ''; + $request->print("\n\n". + '
'. + '
'.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).'
'. + '
'."\n"); + + if ($env{'form.vProb'} eq 'all' or $env{'form.vAns'} eq 'all') { + my $mode; + if ($env{'form.vProb'} eq 'all' && $env{'form.vAns'} eq 'all') { + $mode='both'; + } elsif ($env{'form.vProb'} eq 'all' ) { + $mode='text'; + } elsif ($env{'form.vAns'} eq 'all') { + $mode='answer'; + } + &Apache::lonxml::clear_problem_counter(); + $request->print(&show_problem($request,$symb,$uname,$udom,1,1,$mode,{'request.prefix' => 'ctr'.$counter})); + } + + my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname); + my $res_error; + my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error); + if ($res_error) { + $request->print(&navmap_errormsg()); + return; } - my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname); - my ($partlist,$handgrade) = &response_type($url); # Display student info $request->print(($counter == 0 ? '' : '
')); - my $result='
'."\n". - '\n"; - if ($$timestamp eq '') { - $lastsubonly.='
'."\n"; - - $result.='Fullname: '.$ENV{'form.fullname'}. - '   Username: '.$uname. - ($ENV{'user.domain'} eq $udom ? '' : ' ('.$udom.')').'
'."\n"; + my $result='
'; + + $result.='
' + .'

'.&mt('Submissions').'

'; $result.=''."\n"; + '" value="'.$env{'form.fullname'}.'" />'."\n"; + if ($env{'form.handgrade'} eq 'no') { + $result.='

' + .&mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon) + ."

\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 '; - $result.=(scalar(@badcollaborators) == 1) ? 'an invalid collaborator' : 'invalid collaborators'; - $result .= ': '.join(', ',@badcollaborators); - $result .= '
'; - } - if (scalar(@badcollaborators > $ncol)) { - $result .= '
'; - $result .= 'This student has submitted too many '. - 'collaborators. Maximum is '.$ncol.'.'; - $result .= '
'; - } - } + my $fullname; + my $col_fullnames = []; + if ($env{'form.handgrade'} eq 'yes') { + (my $sub_result,$fullname,$col_fullnames)= + &check_collaborators($symb,$uname,$udom,\%record,$handgrade, + $counter); + $result.=$sub_result; } $request->print($result."\n"); # print student answer/submission - # Options are (1) Handgaded submission only + # Options are (1) Handgraded 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)."
'.$$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

'; - } 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: '.($responsetype =~ /^(essay|option)$/ ? - '
' : ''). - &cleanRecord(&keywords_highlight($subval),$responsetype). - ($responsetype =~ /^(essay|option)$/ ? '

' : - '

').$similar."\n" - if ($ENV{'form.lastSub'} eq 'lastonly' || - ($ENV{'form.lastSub'} eq 'hdgrade' && - $$handgrade{$part} =~ /:yes$/)); + if ($env{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) { + my ($string,$timestamp)= &get_last_submission(\%record); + + my $lastsubonly; + + if ($$timestamp eq '') { + $lastsubonly.='
'.$$string[0].'
'; + } else { + $lastsubonly = '
Date Submitted: '.$$timestamp."\n"; + + my %seenparts; + my @part_response_id = &flatten_responseType($responseType); + foreach my $part (@part_response_id) { + next if ($env{'form.lastSub'} eq 'hdgrade' + && $$handgrade{$$part[0].'_'.$$part[1]} ne 'yes'); + + my ($partid,$respid) = @{ $part }; + my $display_part=&get_display_part($partid,$symb); + if ($env{"form.$uname:$udom:$partid:submitted_by"}) { + if (exists($seenparts{$partid})) { next; } + $seenparts{$partid}=1; + my $submitby='Part: '.$display_part. + ' Collaborative submission by: '. + ''. + $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'
'; + $request->print($submitby); + next; + } + my $responsetype = $responseType->{$partid}->{$respid}; + if (!exists($record{"resource.$partid.$respid.submission"})) { + $lastsubonly.="\n".'
'. + ''.&mt('Part: [_1]',$display_part).''. + ' '. + '('.&mt('Part ID: [_1]',$respid).')'. + '   '. + ''.&mt('Nothing submitted - no attempts.').'

'; + next; + } + foreach my $submission (@$string) { + my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/); + if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; } + my ($ressub,$subval) = split(/:/,$submission,2); + # Similarity check + my $similar=''; + if($env{'form.checkPlag'}){ + my ($oname,$odom,$ocrsid,$oessay,$osim)= + &most_similar($uname,$udom,$subval,\%old_essays); + if ($osim) { + $osim=int($osim*100.0); + my %old_course_desc = + &Apache::lonnet::coursedescription($ocrsid, + {'one_time' => 1}); + + $similar="

". + &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])', + $osim, + &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')', + $old_course_desc{'description'}, + $old_course_desc{'num'}, + $old_course_desc{'domain'}). + '

'. + &keywords_highlight($oessay). + '

'; + } + } + my $order=&get_order($partid,$respid,$symb,$uname,$udom); + if ($env{'form.lastSub'} eq 'lastonly' || + ($env{'form.lastSub'} eq 'hdgrade' && + $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) { + my $display_part=&get_display_part($partid,$symb); + $lastsubonly.='
'. + ''.&mt('Part: [_1]',$display_part).''. + ' '. + '('.&mt('Part ID: [_1]',$respid).')'. + '   '; + my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record); + if (@$files) { + $lastsubonly.='
'.&mt('Like all files provided by users, this file may contain viruses').'
'; + my $file_counter = 0; + foreach my $file (@$files) { + $file_counter++; + &Apache::lonnet::allowuploaded('/adm/grades',$file); + $lastsubonly.='
'.$file.''; } + $lastsubonly.='
'; } + $lastsubonly.=''.&mt('Submitted Answer:').' '. + &cleanRecord($subval,$responsetype,$symb,$partid, + $respid,\%record,$order,undef,$uname,$udom); + if ($similar) {$lastsubonly.="

$similar\n";} + $lastsubonly.='
'; } } } - $lastsubonly.='
'."\n"; - $request->print($lastsubonly); + $lastsubonly.=''."\n"; # End: LC_grade_submissions_body } - } else { + $request->print($lastsubonly); + } elsif ($env{'form.lastSub'} eq 'datesub') { + my (undef,$responseType,undef,$parts) = &showResourceInfo($symb); + $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom)); + } elsif ($env{'form.lastSub'} =~ /^(last|all)$/) { $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom, - $ENV{'request.course.id'}, + $env{'request.course.id'}, $last,'.submission', 'Apache::grades::keywords_highlight')); } $request->print(''."\n"); - # return if view submission with no grading option - if ($ENV{'form.showgrading'} eq '' || (!&canmodify($usec))) { + if ($env{'form.showgrading'} eq '' || (!&canmodify($usec))) { my $toGrade.='  '."\n" if (&canmodify($usec)); - $toGrade.='
'."\n"; - $toGrade.=&show_grading_menu_form($symb,$url) - if (($ENV{'form.command'} eq 'submission') || - ($ENV{'form.command'} eq 'processGroup' && $counter == $total)); - $request = print($toGrade); + 'onclick="javascript:checksubmit(this.form,\'Grade Student\',\'' + .$counter.'\');" target="_self" />  '."\n" if (&canmodify($usec)); + $toGrade.='
'."\n"; + if (($env{'form.command'} eq 'submission') || + ($env{'form.command'} eq 'processGroup' && $counter == $total)) { + $toGrade.=''.&show_grading_menu_form($symb); + } + $request->print($toGrade); return; + } else { + $request->print('
'."\n"); } # essay grading message center - if ($ENV{'form.handgrade'} eq 'yes') { - my ($lastname,$givenn) = split(/,/,$ENV{'form.fullname'}); + if ($env{'form.handgrade'} eq 'yes') { + my $result='
'; + + $result.='
'. + &mt('Send Message').'
'; + my ($lastname,$givenn) = split(/,/,$env{'form.fullname'}); my $msgfor = $givenn.' '.$lastname; - if (scalar(@col_fullnames) > 0) { - my $lastone = pop @col_fullnames; - $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.'; + if (scalar(@$col_fullnames) > 0) { + my $lastone = pop(@$col_fullnames); + $msgfor .= ', '.(join ', ',@$col_fullnames).' and '.$lastone.'.'; } $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript -# $result.=''."\n". - $result=''."\n". + $result.=''."\n". ''."\n"; $result.=' '. - 'Compose Message to student'.(scalar(@col_fullnames) >= 1 ? 's' : '').'  '. + ',\''.$msgfor.'\');" target="_self">'. + &mt('Compose message to student').(scalar(@$col_fullnames) >= 1 ? 's' : '').')'. ''."\n". - '
 (Message will be sent when you click on Save & Next below.)'."\n" - if ($ENV{'form.handgrade'} eq 'yes'); + '
 ('. + &mt('Message will be sent when you click on Save & Next below.').")\n"; + $result.='
'; $request->print($result); } my %seen = (); my @partlist; - for (sort keys(%$handgrade)) { - my ($partid,$respid) = split(/_/); + my @gradePartRespid; + my @part_response_id = &flatten_responseType($responseType); + $request->print( + '
' + .'

'.&mt('Assign Grades').'

' + ); + $request->print(&gradeBox_start()); #
+ foreach my $part_response_id (@part_response_id) { + my ($partid,$respid) = @{ $part_response_id }; + my $part_resp = join('_',@{ $part_response_id }); next if ($seen{$partid} > 0); $seen{$partid}++; - next if ($$handgrade{$_} =~ /:no$/ && $ENV{'form.lastSub'} =~ /^(hdgrade)$/); - push @partlist,$partid; - + next if ($$handgrade{$part_resp} ne 'yes' + && $env{'form.lastSub'} eq 'hdgrade'); + push(@partlist,$partid); + push(@gradePartRespid,$partid.'.'.$respid); $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record)); } + $request->print(&gradeBox_end()); #
+ $request->print('
'); + + $request->print(''); + $result=''."\n"; + $result.=''."\n" if ($counter == 0); my $ctr = 0; while ($ctr < scalar(@partlist)) { $result.=''."\n"; $ctr++; } - $request->print($result.''."\n"); + $request->print($result.''."\n"); + +# Done with printing info for one student + + $request->print('');#LC_grade_show_user_body + $request->print('');#LC_grade_show_user + # print end of form if ($counter == $total) { my $endform='
'."\n"; - $endform.='  '."\n"; + $endform.='  '."\n"; my $ntstu =''."\n"; - my $nsel = ($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1'); - $ntstu =~ s/
'; - $endform.=&show_grading_menu_form($symb,$url); + my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1'); + $ntstu =~ s/