--- loncom/homework/grades.pm 2006/02/27 19:40:21 1.320 +++ loncom/homework/grades.pm 2010/03/15 07:54:04 1.598 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.320 2006/02/27 19:40:21 albertel Exp $ +# $Id: grades.pm,v 1.598 2010/03/15 07:54:04 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,6 +26,8 @@ # http://www.lon-capa.org/ # + + package Apache::grades; use strict; use Apache::style; @@ -35,42 +37,85 @@ 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; + +} +# +# 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.--- +# Returns an array of everything that the resources stores away +# + sub getpartlist { - my ($url,$symb) = @_; - my $partorder = &Apache::lonnet::metadata($url, 'partorder'); - my @parts; - if ($partorder) { - for my $part (split (/,/,$partorder)) { - if (!&Apache::loncommon::check_if_partid_hidden($part,$symb)) { - push(@parts, $part); - } - } - } else { - my $metadata = &Apache::lonnet::metadata($url, 'packages'); - foreach (split(/\,/,$metadata)) { - if ($_ =~ /^part_(.*)$/) { - if (!&Apache::loncommon::check_if_partid_hidden($1,$symb)) { - push(@parts, $1); - } - } - } + 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 (@parts) { - my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys')); + foreach my $part (@{ $partlist }) { foreach my $key (@metakeys) { if ($key =~ m/^stores_\Q$part\E_/) { push(@stores,$key); } } @@ -79,17 +124,22 @@ sub getpartlist { } # --- Get the symbolic name of a problem and the url -sub get_symb_and_url { +# Generate an error message if symb could not be found unless silent flag is set +# Takes $env{'form.symb'} by default; if not present, takes $env{'form.url'} and tries to get symb from that +# + +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:."); + $request->print(&mt("Unable to handle ambiguous references: [_1].",$url)); return (); } } - return ($symb,$url); + &Apache::lonenc::check_decrypt(\$symb); + return ($symb); } #--- Format fullname, username:domain if different for display @@ -97,57 +147,67 @@ sub get_symb_and_url { sub nameUserString { my ($type,$fullname,$uname,$udom) = @_; if ($type eq 'header') { - return ' Fullname (Username)'; + return ' '.&mt('Fullname').' ('.&mt('Username').')'; } else { - return ' '.$fullname.' ('.$uname. - ($env{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')'; + return ' '.$fullname.' ('.$uname. + ($env{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')'; } } #--- Get the partlist and the response type for a given problem. --- #--- Indicate if a response type is coded handgraded or not. --- sub response_type { - my ($url,$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 %vPart; - foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) { - $vPart{$partid}=1; - } - my %seen = (); - my (@partlist,%handgrade,%responseType); - foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) { - if (/^\w+response_.*/) { - my ($responsetype,$part) = split(/_/,$_,2); - my ($partid,$respid) = split(/_/,$part); - if (&Apache::loncommon::check_if_partid_hidden($partid,$symb)) { - next; - } - if (%vPart && !exists($vPart{$partid})) { - next; - } - $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!! - my ($value) = &Apache::lonnet::EXT('resource.'.$part.'.handgrade',$symb); - $handgrade{$part} = ($value eq 'yes' ? 'yes' : 'no'); - if (!exists($responseType{$partid})) { $responseType{$partid}={}; } - $responseType{$partid}->{$respid}=$responsetype; - 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; } - return \@partlist,\%handgrade,\%responseType; + my $res = $navmap->getBySymb($symb); + unless (ref($res)) { + $$response_error = 1; + return; + } + 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,$url,$symb)=@_; - if (!defined($symb) || $symb eq '') { - $symb=$env{'form.symb'}; - if ($symb eq '') { $symb=&Apache::lonnet::symbread($url) } - } + my ($partID,$symb)=@_; my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb); if (defined($display) and $display ne '') { - $display.= " (id $partID)"; + $display.= ' (' + .&mt('Part ID: [_1]',$partID).')'; } else { $display=$partID; } @@ -156,62 +216,164 @@ sub get_display_part { #--- Show resource title #--- and parts and response type -sub showResourceInfo { - my ($url,$probTitle,$checkboxes) = @_; - my $col=3; - if ($checkboxes) { $col=4; } - my $result =''. - ''."\n"; - my ($partlist,$handgrade,$responseType) = &response_type($url); - my %resptype = (); - my $hdgrade='no'; - my %partsseen; - for my $part_resID (sort keys(%$handgrade)) { - my $handgrade=$$handgrade{$part_resID}; - my ($partID,$resID) = split(/_/,$part_resID); - my $responsetype = $responseType->{$partID}->{$resID}; - $hdgrade = $handgrade if ($handgrade eq 'yes'); - $result.=''; - if ($checkboxes) { - if (exists($partsseen{$partID})) { - $result.=""; - } else { - $result.=""; +#sub showResourceInfo { +# 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; + } } - $partsseen{$partID}=1; } - my $display_part=&get_display_part($partID,$url); - $result.=''. - ''; -# ''; } - $result.='
'.&mt('Current Resource').': '. - $probTitle.'
  '.&mt('Problem Part').''.&mt('Res. ID').''.&mt('Type').' '.$display_part.''.''.$resID.''.&mt($responsetype).''.&mt('Handgrade: [_1]',$handgrade).'Part: '.$display_part.' '. - $resID.'Type: '.$responsetype.'
Handgrade: '.$handgrade.'
'."\n"; - return $result,$responseType,$hdgrade,$partlist,$handgrade; -} + 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); + } -sub get_order { - my ($partid,$respid,$symb,$uname,$udom)=@_; - my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb); - $url=&Apache::lonnet::clutter($url); - my $subresult=&Apache::lonnet::ssi($url, - ('grade_target' => 'analyze'), - ('grade_domain' => $udom), - ('grade_symb' => $symb), - ('grade_courseid' => - $env{'request.course.id'}), - ('grade_username' => $uname)); - (undef,$subresult)=split(/_HASH_REF__/,$subresult,2); - my %analyze=&Apache::lonnet::str2hash($subresult); - return ($analyze{"$partid.$respid.shown"}); } + #--- Clean response type for display -#--- Currently filters option/rank/radiobutton/match/essay response types only. +#--- Currently filters option/rank/radiobutton/match/essay/Task +# response types only. sub cleanRecord { - my ($answer,$response,$symb,$partid,$respid,$record,$order,$version) = @_; - my $grayFont = ''; + 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"}); @@ -222,11 +384,11 @@ sub cleanRecord { } else { $toprow.=''.$answer{$foil}.' '; } - $bottomrow.=''.$grayFont.$foil.' '; + $bottomrow.=''.$grayFont.$foil.' '; } return '
'. - ''.$toprow.''. - ''. + ''.$toprow.''. + ''. $grayFont.$bottomrow.''.'
Answer
'.$grayFont.'Option ID
'.&mt('Answer').'
'.$grayFont.&mt('Option ID').'
'; } elsif ($response eq 'match') { my %answer=&Apache::lonnet::str2hash($answer); @@ -237,40 +399,40 @@ sub cleanRecord { my $item=shift(@items); if ($grading{$foil} == 1) { $toprow.=''.$item.' '; - $middlerow.=''.$grayFont.$answer{$foil}.' '; + $middlerow.=''.$grayFont.$answer{$foil}.' '; } else { $toprow.=''.$item.' '; - $middlerow.=''.$grayFont.$answer{$foil}.' '; + $middlerow.=''.$grayFont.$answer{$foil}.' '; } - $bottomrow.=''.$grayFont.$foil.' '; + $bottomrow.=''.$grayFont.$foil.' '; } return '
'. - ''.$toprow.''. - ''. + ''.$toprow.''. + ''. $middlerow.''. - ''. + ''. $bottomrow.''.'
Answer
'.$grayFont.'Item ID
'.&mt('Answer').'
'.$grayFont.&mt('Item ID').'
'.$grayFont.'Option ID
'.$grayFont.&mt('Option ID').'
'; } elsif ($response eq 'radiobutton') { my %answer=&Apache::lonnet::str2hash($answer); my ($toprow,$bottomrow); - my $correct=($order->[0])+1; - for (my $i=1;$i<=$#$order;$i++) { - my $foil=$order->[$i]; + my $correct = + &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom); + foreach my $foil (@$order) { if (exists($answer{$foil})) { - if ($i == $correct) { - $toprow.='true'; + if ($foil eq $correct) { + $toprow.=''.&mt('true').''; } else { - $toprow.='true'; + $toprow.=''.&mt('true').''; } } else { - $toprow.='false'; + $toprow.=''.&mt('false').''; } - $bottomrow.=''.$grayFont.$foil.' '; + $bottomrow.=''.$grayFont.$foil.' '; } return '
'. - ''.$toprow.''. - ''. - $grayFont.$bottomrow.''.'
Answer
'.$grayFont.'Option ID
'; + ''.&mt('Answer').''.$toprow.''. + ''.$grayFont.&mt('Option ID').''. + $bottomrow.''.''; } elsif ($response eq 'essay') { if (! exists ($env{'form.'.$symb})) { my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade', @@ -291,6 +453,40 @@ sub cleanRecord { 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; } @@ -298,8 +494,7 @@ sub cleanRecord { #-- A couple of common js functions sub commonJSfunctions { my $request = shift; - $request->print(< + $request->print(&Apache::lonhtmlcommon::scripttag(< 1) { @@ -327,15 +522,16 @@ sub commonJSfunctions { return selectOne.value; } } - COMMONJSFUNCTIONS } #--- Dumps the class list with usernames,list of sections, #--- section, ids and fullnames for each user. sub getclasslist { - my ($getsec,$filterlist) = @_; + 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); @@ -344,10 +540,19 @@ sub getclasslist { @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=&Apache::loncoursedata::get_classlist(); + 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; @@ -364,18 +569,40 @@ sub getclasslist { $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->{$student}); + if ($filterlist && (!($stu_status =~ /Any/))) { + if (!($stu_status =~ $status)) { + delete($classlist->{$student}); next; } } + # 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 || grep(/^\Q$section\E$/,@getsec)) { $sections{$section}++; - $fullnames{$student}=$fullname; + if ($classlist->{$student}) { + $fullnames{$student}=$fullname; + } } else { delete($classlist->{$student}); } @@ -430,7 +657,7 @@ sub canview { #--- Retrieve the grade status of a student for all the parts sub student_gradeStatus { - my ($url,$symb,$udom,$uname,$partlist) = @_; + my ($symb,$udom,$uname,$partlist) = @_; my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname); my %partstatus = (); foreach (@$partlist) { @@ -447,20 +674,20 @@ sub student_gradeStatus { # Use by verifyscript and viewgrades # Shows a student's view of problem and submission sub jscriptNform { - my ($url,$symb) = @_; - my $jscript=''."\n"; + "\n"); $jscript.= '
'."\n". - ''."\n". - ''."\n". + ''."\n". ''."\n". ''."\n". - ''."\n". + ''."\n". ''."\n". ''."\n". ''."\n". @@ -468,6 +695,8 @@ 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. @@ -502,7 +731,7 @@ sub compute_points { # sub most_similar { - my ($uname,$udom,$uessay)=@_; + my ($uname,$udom,$uessay,$old_essays)=@_; # ignore spaces and punctuation @@ -510,7 +739,7 @@ sub most_similar { # ignore empty submissions (occuring when only files are sent) - unless ($uessay=~/\w+/) { return ''; } + unless ($uessay=~/\w+/s) { return ''; } # these will be returned. Do not care if not at least 50 percent similar my $limit=0.6; @@ -519,23 +748,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); @@ -556,23 +784,42 @@ sub verifyreceipt { 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'); my $receiptparts=0; - if ($env{"course.$courseid.receiptalg"} eq 'receipt2') { $receiptparts=1; } + if ($env{"course.$courseid.receiptalg"} eq 'receipt2' || + $env{"course.$courseid.receiptalg"} eq 'receipt3') { $receiptparts=1; } my $parts=['0']; - if ($receiptparts) { ($parts)=&response_type($url,$symb); } + 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})) { @@ -583,38 +830,38 @@ sub verifyreceipt { my ($uname,$udom)=split(/\:/); foreach my $part (@$parts) { if ($receipt eq &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb,$part)) { - $contents.=' '."\n". + $contents.= + &Apache::loncommon::start_data_table_row(). + ' '."\n". ''.$$fullname{$_}.' '."\n". + '\');" target="_self">'.$$fullname{$_}.' '."\n". ' '.$uname.' '. ' '.$udom.' '; if ($receiptparts) { $contents.=' '.$part.' '; } - $contents.=''."\n"; + $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". - ''; - if ($receiptparts) { - $string.=''; - } - $string.=''."\n".$contents. - '
 Fullname  Username  Domain  Problem Part 
'."\n"; + $string = &jscriptNform($symb).$title. + '

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

'. + $header. + $contents. + &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. @@ -624,23 +871,30 @@ sub verifyreceipt { sub listStudents { my ($request) = shift; - my ($symb,$url) = &get_symb_and_url($request); + 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='

 '.$viewgrade. - ' Submissions for a Student or a Group of Students

'; - - my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes')); - - $request->print(< + 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 ($partlist,$handgrade,$responseType) = &response_type($symb +#,$res_error + ); + + 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(&Apache::lonhtmlcommon::scripttag(< LISTJAVASCRIPT &commonJSfunctions($request); $request->print($result); - my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked' : ''; - my $checklastsub = $checkhdgrade eq '' ? 'checked' : ''; + my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked="checked"' : ''; + my $checklastsub = $checkhdgrade eq '' ? 'checked="checked"' : ''; my $gradeTable=''. - "\n".$table. - ' View Problem Text: '."\n". - ''."\n". - '
'."\n". - ' View Answer: '."\n". - ''."\n". - '
'."\n". - ' Submissions: '."\n"; + "\n"; + + $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) { - $gradeTable.=''."\n"; + $submission_options.= + ''."\n"; } - - my $saveStatus = $env{'form.Status'} eq '' ? 'Active' : $env{'form.Status'}; + 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.=''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". + $gradeTable .= + &build_section_inputs(). ''."\n". '
'."\n". '
'."\n". ''."\n". ''."\n". - ''."\n". - ''."\n". + ''."\n". ''."\n"; if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) { - $gradeTable.=''."\n"; + $gradeTable .= ''."\n"; } else { - $gradeTable.='Student Status: '. - &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);').'
'; + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status')) + .&Apache::lonhtmlcommon::StatusOptions( + $saveStatus,undef,1,'javascript:reLoadList(this.form);') + .&Apache::lonhtmlcommon::row_closure(); } - $gradeTable.='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". - ''."\n"; + $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" + .'' + .'

'; # checkall buttons $gradeTable.=&check_script('gradesub', 'stuinfo'); $gradeTable.='
'."\n"; + 'onclick="javascript:checkSelect(this.form.stuinfo);" '."\n". + 'value="'.&mt('Next').' →" />
'."\n"; $gradeTable.=&check_buttons(); - $gradeTable.=''; - my ($classlist, undef, $fullname) = &getclasslist($getsec,'1'); - $gradeTable.='
'. - ''; + 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.=''. - ''; + $gradeTable.=''. + ''; if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'queued' && $submitonly ne 'all') { - foreach (sort(@$partlist)) { - my $display_part=&get_display_part((split(/_/))[0],$url,$symb); - $gradeTable.=''; + foreach my $part (sort(@$partlist)) { + my $display_part= + &get_display_part((split(/_/,$part))[0],$symb); + $gradeTable.= + ''; } } elsif ($submitonly eq 'queued') { - $gradeTable.=''; + $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 @@ -772,7 +1062,7 @@ LISTJAVASCRIPT if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'queued' && $submitonly ne 'all') { - (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist); + (%status) =&student_gradeStatus($symb,$udom,$uname,$partlist); my $submitted = 0; my $graded = 0; my $incorrect = 0; @@ -800,24 +1090,28 @@ LISTJAVASCRIPT $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); + if ($ctr%2 ==1) { + $gradeTable.= &Apache::loncommon::start_data_table_row(); + } $gradeTable.=''. - ''."\n".''."\n"; + ' '.$section.($group ne '' ?'/'.$group:'').''."\n"; if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { - foreach (sort keys(%status)) { - next if (/^resource.*?submitted_by$/); - $gradeTable.=''."\n"; + foreach (sort(keys(%status))) { + next if ($_ =~ /^resource.*?submitted_by$/); + $gradeTable.=''."\n"; } } # $gradeTable.='' if ($ctr%2 ==1); - $gradeTable.=''."\n" if ($ctr%2 ==0); + if ($ctr%2 ==0) { + $gradeTable.=&Apache::loncommon::end_data_table_row()."\n"; + } } } if ($ctr%2 ==1) { @@ -831,30 +1125,31 @@ LISTJAVASCRIPT } elsif ($submitonly eq 'queued') { $gradeTable.=''; } - $gradeTable.=''; + $gradeTable.=&Apache::loncommon::end_data_table_row(); } - $gradeTable.='
 No.  Select '.&nameUserString('header').' Section/Group'.&mt('No.').''.&mt('Select').''.&nameUserString('header').' '.&mt('Section/Group').' Part: '.$display_part. - ' Status '.&mt('Part: [_1] Status',$display_part).' '.&mt('Queue Status').' '.&mt('Queue Status').' 
'.$ctr.' '. &nameUserString(undef,$$fullname{$student},$uname,$udom). - ' '.$section.' '.$status{$_}.'  '.&mt($status{$_}).' 
 
'."\n". - ''."\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 { 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='
 '. - 'No '.$submissions.' found for this resource for any students. ('.$num_students. - ' students checked for '.$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 ''; } @@ -863,7 +1158,7 @@ LISTJAVASCRIPT sub check_script { my ($form, $type)=@_; - my $chkallscript=''."\n"; +'."\n"); return $chkallscript; } sub check_buttons { - my $buttons.=''; - $buttons.=' '; - $buttons.=''; + my $buttons.=''; + $buttons.=' '; + $buttons.=''; $buttons.=' '; return $buttons; } @@ -913,8 +1208,8 @@ sub processGroup { my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo'); my $total = scalar(@stuchecked)-1; - foreach (@stuchecked) { - my ($uname,$udom,$fullname) = split(/:/); + foreach my $student (@stuchecked) { + my ($uname,$udom,$fullname) = split(/:/,$student); $env{'form.student'} = $uname; $env{'form.userdom'} = $udom; $env{'form.fullname'} = $fullname; @@ -932,8 +1227,8 @@ sub processGroup { #--- Javascript to handle the submission page functionality --- sub sub_page_js { my $request = shift; - $request->print(< + my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = '); + $request->print(&Apache::lonhtmlcommon::scripttag(< SUBJAVASCRIPT } @@ -1110,10 +1404,81 @@ sub sub_page_kw_js { my $request = shift; my $iconpath = $request->dir_config('lonIconsURL'); &commonJSfunctions($request); + + my $inner_js_msg_central= &Apache::lonhtmlcommon::scripttag(< 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\.//; - $request->print(< + my $alertmsg = &mt('Please select a word or group of words from document and then click this link.'); + $request->print(&Apache::lonhtmlcommon::scripttag(<"); - 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,$respid) = @_; - my $checkIcon = ''; + my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_; + 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 '' ? '' : &compute_points($$record{'resource.'.$partid.'.awarded'},$wgt)); my $result=''."\n"; - $result.='
'.$partid.' - '.$respid.'
'; - my $display_part=&get_display_part($partid,undef,$symb); + 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.=''."\n"; - $result.=''."\n"; - $result.=''."\n"; + $line.='',$display_part,$radio,$line); + $result .= + ''; + $result.=&Apache::loncommon::end_data_table_row(); $result.=''."\n". ''."\n". ''."\n". ''."\n"; - $result.='
'. - 'Part: '.$display_part.' Points: '."\n"; + $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.='
'."\n"; # display radio buttons in a nice table 10 across + while ($thisweight<=$wgt) { + $radio.= '\n"; + $radio.=(($ctr+1)%10 == 0 ? '' : ''); + $thisweight += $increment; $ctr++; } - $result.='
'; - $result.='
 or /'.$wgt.' '.$wgtmsg. + $line.='/'.$wgt.' '.$wgtmsg. ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? ' '.$checkIcon : ''). - ' '."\n"; - $result.=''."\n"; - $result.="  \n"; + $line.=''."\n"; + + + #&mt('Part:[_1]Points:[_2]or[_3]'.$display_part.''.$radio.''.&mt('or').''.$line.'
'."\n"; - $result.=&handbackBox($uname,$udom,$counter,$partid,$record,$respid); + my $res_error; + $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record,\$res_error); + if ($res_error) { + return &navmap_errormsg(); + } return $result; } -sub handbackBox { - my ($uname,$udom,$counter,$partid,$record,$respid) = @_; - my $files=&get_submitted_files($udom,$uname,$partid,$respid,$record); + +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; - if (@$files) { - my $file_counter = 0; + 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) { - $result.=' Return commented document to student. '."\n"; - $result.=''; - $result.='
'; - } + 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,$mode) = @_; + 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'}); + $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') { - $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom, - $env{'request.course.id'}); + &Apache::lonxml::restore_problem_counter(); + $companswer= + &Apache::loncommon::get_student_answers($symb,$uname,$udom, + $env{'request.course.id'}, + %form); } if ($removeform) { $companswer=~s|||g; $companswer=~s|||g; $companswer=~s|name="submit"|name="would_have_been_submit"|g; } - my $result.='
'; - $result.=''; - if ($viewon) { - $result.=''; - } + $rendered= + '
' + .'

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

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

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

' + .$companswer + .'
'; + my $result; if ($mode eq 'both') { - $result.='
'; - if ($mode eq 'both' or $mode eq 'text') { - $result.='View of the problem - '; - } else { - $result.='Correct answer: '; - } - $result.=$env{'form.fullname'}.'
'.$rendered.'
'; - $result.='Correct answer:
'.$companswer; + $result=$rendered.$companswer; } elsif ($mode eq 'text') { - $result.='
'.$rendered; + $result=$rendered; } elsif ($mode eq 'answer') { - $result.='
'.$companswer; + $result=$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'} = &Apache::loncommon::plainname($uname,$udom,'lastname') 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 $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.' in section '.$usec.' in course id '. - $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; } @@ -1562,24 +1971,22 @@ sub submission { 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 = ''; + 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 $checkMark='

 Note: Part(s) graded correct by the computer is marked with a '. - $checkIcon.' symbol.'."\n"; - $request->print($checkMark); + 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. @@ -1592,9 +1999,10 @@ sub submission { } 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 = (); @@ -1613,25 +2021,24 @@ sub submission { $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". + &build_section_inputs(). + ''."\n". + ''."\n". ''."\n"); if ($env{'form.handgrade'} eq 'yes') { @@ -1665,24 +2072,49 @@ sub submission { # $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); } } +# 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" + ); + + # Show additional functions if allowed + if ($perm{'vgr'}) { + $request->print( + &Apache::loncommon::track_student_link( + &mt('View recent activity'), + $uname,$udom,'check') + .' ' + ); + } + if ($perm{'opa'}) { + $request->print( + &Apache::loncommon::pprmlink( + &mt('Set/Change parameters'), + $uname,$udom,$symb,'check')); + } + + # Show Problem if ($env{'form.vProb'} eq 'all' or $env{'form.vAns'} eq 'all') { - $request->print('


') if ($counter > 0); my $mode; if ($env{'form.vProb'} eq 'all' && $env{'form.vAns'} eq 'all') { $mode='both'; @@ -1691,97 +2123,68 @@ KEYWORDS } elsif ($env{'form.vAns'} eq 'all') { $mode='answer'; } - $request->print(&show_problem($request,$symb,$uname,$udom,1,1,$mode)); + &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 ($partlist,$handgrade,$responseType) = &response_type($url,$symb); + my $res_error; + my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error); + if ($res_error) { + $request->print(&navmap_errormsg()); + return; + } # Display student info $request->print(($counter == 0 ? '' : '
')); - my $result='
'."\n". - '\n"; - if ($$timestamp eq '') { - $lastsubonly.='
'."\n"; - $result.='Fullname: '.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).'
'."\n"; + my $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); + my $fullname; + my $col_fullnames = []; 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"; - my ($part)=split(/\./,$_); - $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 $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)$/) { my ($string,$timestamp)= &get_last_submission(\%record); - my $lastsubonly=''. - ($$timestamp eq '' ? '' : 'Date Submitted: '. - $$timestamp)."
'.$$string[0]; - } else { + + my $lastsubonly; + + if ($$timestamp eq '') { + $lastsubonly.='
'.$$string[0].'
'; + } else { + $lastsubonly = + '
' + .''.&mt('Date Submitted:').' '.$$timestamp."\n"; + my %seenparts; - for my $part (sort keys(%$handgrade)) { - my ($partid,$respid) = split(/_/,$part); - my $display_part=&get_display_part($partid,$url,$symb); + 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; @@ -1789,69 +2192,95 @@ KEYWORDS ' Collaborative submission by: '. ''. + '\');" target="_self">'. $$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.='
Part: '. - $display_part.' ( ID '.$respid. - ' )   '. - 'Nothing submitted - no attempts

'; + $lastsubonly.="\n".'
'. + ''.&mt('Part: [_1]',$display_part).''. + ' '. + '('.&mt('Part ID: [_1]',$respid).')'. + '   '. + ''.&mt('Nothing submitted - no attempts.').'

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

Essay". - " is $osim% similar to an essay by ". - &Apache::loncommon::plainname($oname,$odom). - '

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

'; + my %old_course_desc = + &Apache::lonnet::coursedescription($ocrsid, + {'one_time' => 1}); + + if ($hide) { + $similar='
'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'
'. + &mt('As the current submission is for an anonymous survey, no other details are available.').'

'; + } else { + $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} eq 'yes')) { - my $display_part=&get_display_part($partid,$url,$symb); - $lastsubonly.='
Part: '. - $display_part.' ( ID '.$respid. - ' )   '; + $$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.='
Like all files provided by users, this file may contain virusses
'; - my $file_counter = 0; - foreach my $file (@$files) { - $file_counter ++; - &Apache::lonnet::allowuploaded('/adm/grades',$file); - $lastsubonly.='
'.$file.''; - } + if ($hide) { + $lastsubonly.='
'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files})); + } else { + $lastsubonly.='
'.&mt('Like all files provided by users, this file may contain viruses').'
'; + foreach my $file (@$files) { + &Apache::lonnet::allowuploaded('/adm/grades',$file); + $lastsubonly.='
'.$file.''; + } + } $lastsubonly.='
'; } - $lastsubonly.='Submitted Answer: '. - &cleanRecord($subval,$responsetype,$symb,$partid, - $respid,\%record,$order); + if ($hide) { + $lastsubonly.=''.&mt('Anonymous Survey').''; + } else { + $lastsubonly.=''.&mt('Submitted Answer:').' '. + &cleanRecord($subval,$responsetype,$symb,$partid, + $respid,\%record,$order,undef,$uname,$udom); + } if ($similar) {$lastsubonly.="

$similar\n";} + $lastsubonly.='
'; } } } + $lastsubonly.=''."\n"; # End: LC_grade_submissions_body } - $lastsubonly.='
'."\n"; $request->print($lastsubonly); - } elsif ($env{'form.lastSub'} eq 'datesub') { - my (undef,$responseType,undef,$parts) = &showResourceInfo($url); + } elsif ($env{'form.lastSub'} eq 'datesub') { +# my (undef,$responseType,undef,$parts) = &showResourceInfo($symb); + my ($parts,$handgrade,$responseType) = &response_type($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, @@ -1862,71 +2291,75 @@ KEYWORDS $request->print(''."\n"); - # return if view submission with no grading option if ($env{'form.showgrading'} eq '' || (!&canmodify($usec))) { my $toGrade.='  '."\n" if (&canmodify($usec)); - $toGrade.='
'."\n"; + '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,$url) + $toGrade.=''.&show_grading_menu_form($symb); } $request->print($toGrade); return; } else { - $request->print(''."\n"); + $request->print(''."\n"); } # essay grading message center 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". ''."\n"; $result.=' '. - &mt('Compose message to student').(scalar(@col_fullnames) >= 1 ? 's' : '').' ('. - &mt('incl. grades').' )'. + ',\''.$msgfor.'\');" target="_self">'. + &mt('Compose message to student').(scalar(@$col_fullnames) >= 1 ? 's' : '').')'. ''."\n". '
 ('. - &mt('Message will be sent when you click on Save & Next below.').")\n"; + &mt('Message will be sent when you click on Save & Next below.').")\n"; + $result.='
'; $request->print($result); } - if ($perm{'vgr'}) { - $request->print('
'. - &Apache::loncommon::track_student_link(&mt('View recent activity'), - $uname,$udom,'check')); - } - if ($perm{'opa'}) { - $request->print('
'. - &Apache::loncommon::pprmlink(&mt('Set/Change parameters'), - $uname,$udom,$symb,'check')); - } my %seen = (); my @partlist; my @gradePartRespid; - for my $part_resp(sort keys(%$handgrade)) { - my ($partid,$respid) = split(/_/, $part_resp); - #next if ($seen{$partid} > 0); - if ($seen{$partid} > 0) { - $request->print(&handbackBox($uname,$udom,$counter,$partid,\%record,$respid)); - next; - } + 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{$part_resp} =~ /:no$/ && $env{'form.lastSub'} =~ /^(hdgrade)$/); - push @partlist,$partid; - push @gradePartRespid,$partid.'.'.$respid; - $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record,$respid)); + 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"; $ctr++; } - $request->print($result.''."\n"); + $request->print($result.''."\n"); + +# Done with printing info for one student + + $request->print('');#LC_grade_show_user + # print end of form if ($counter == $total) { - my $endform='
'."\n"; - $endform.='  '."\n"; + my $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); + $ntstu =~ s/
'; + $endform.=&show_grading_menu_form($symb); $request->print($endform); } return ''; } +sub check_collaborators { + my ($symb,$uname,$udom,$record,$handgrade,$counter) = @_; + my ($result,@col_fullnames); + my ($classlist,undef,$fullname) = &getclasslist('all','0'); + foreach my $part (keys(%$handgrade)) { + my $ncol = &Apache::lonnet::EXT('resource.'.$part. + '.maxcollaborators', + $symb,$udom,$uname); + next if ($ncol <= 0); + $part =~ s/\_/\./g; + next if ($record->{'resource.'.$part.'.collaborators'} eq ''); + my (@good_collaborators, @bad_collaborators); + foreach my $possible_collaborator + (split(/,?\s+/,$record->{'resource.'.$part.'.collaborators'})) { + $possible_collaborator =~ s/[\$\^\(\)]//g; + next if ($possible_collaborator eq ''); + my ($co_name,$co_dom) = split(/\@|:/,$possible_collaborator); + $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(/^\Q$co_name\E:\Q$co_dom\E$/i, + keys(%$classlist)); + if (! scalar(@matches)) { + push(@bad_collaborators, $possible_collaborator); + } else { + push(@good_collaborators, @matches); + } + } + if (scalar(@good_collaborators) != 0) { + $result.='
'.&mt('Collaborators: '); + foreach my $name (@good_collaborators) { + my ($lastname,$givenn) = split(/,/,$$fullname{$name}); + push(@col_fullnames, $givenn.' '.$lastname); + $result.=$fullname->{$name}.'     '; + } + $result.='
'."\n"; + my ($part)=split(/\./,$part); + $result.=''. + "\n"; + } + if (scalar(@bad_collaborators) > 0) { + $result.='
'; + $result.=&mt('This student has submitted [quant,_1,invalid collaborator]: [_2]',scalar(@bad_collaborators),join(', ',@bad_collaborators)); + $result .= '
'; + } + if (scalar(@bad_collaborators > $ncol)) { + $result .= '
'; + $result .= &mt('This student has submitted too many '. + 'collaborators. Maximum is [_1].',$ncol); + $result .= '
'; + } + } + return ($result,$fullname,\@col_fullnames); +} + #--- Retrieve the last submission for all the parts sub get_last_submission { my ($returnhash)=@_; - my (@string,$timestamp); + my (@string,$timestamp,%lasthidden); if ($$returnhash{'version'}) { my %lasthash=(); my ($version); for ($version=1;$version<=$$returnhash{'version'};$version++) { - foreach (sort(split(/\:/,$$returnhash{$version.':keys'}))) { - $lasthash{$_}=$$returnhash{$version.':'.$_}; - $timestamp = scalar(localtime($$returnhash{$version.':timestamp'})); - } - } - foreach ((keys %lasthash)) { - if ($_ =~ /\.submission$/) { - my ($partid,$foo) = split(/submission$/,$_); - my $draft = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ? - 'Draft Copy ' : ''; - push @string, (join(':',$_,$draft.$lasthash{$_})); - } + foreach my $key (sort(split(/\:/, + $$returnhash{$version.':keys'}))) { + $lasthash{$key}=$$returnhash{$version.':'.$key}; + $timestamp = + &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'}); + } + } + my %typeparts; + my $showsurv = + &Apache::lonnet::allowed('vas',$env{'request.course.id'}); + foreach my $key (sort(keys(%lasthash))) { + if ($key =~ /\.type$/) { + if (($lasthash{$key} eq 'anonsurvey') || + ($lasthash{$key} eq 'anonsurveycred')) { + my ($ign,@parts) = split(/\./,$key); + pop(@parts); + unless ($showsurv) { + my $id = join(',',@parts); + $typeparts{$ign.'.'.$id} = $lasthash{$key}; + } + delete($lasthash{$key}); + } + } + } + my @hidden = keys(%typeparts); + foreach my $key (keys(%lasthash)) { + next if ($key !~ /\.submission$/); + my $hide; + if (@hidden) { + foreach my $id (@hidden) { + if ($key =~ /^\Q$id\E/) { + $hide = 1; + last; + } + } + } + my ($partid,$foo) = split(/submission$/,$key); + my $draft = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ? + 'Draft Copy ' : ''; + push(@string, join(':', $key, $hide, $draft.$lasthash{$key})); } } - @string = $string[0] eq '' ? 'Nothing submitted - no attempts.' : @string; - return \@string,\$timestamp; + if (!@string) { + $string[0] = + ''.&mt('Nothing submitted - no attempts.').''; + } + return (\@string,\$timestamp); } #--- High light keywords, with style choosen by user. @@ -1997,8 +2526,8 @@ sub keywords_highlight { my $styleon = $env{'form.kwstyle'} eq '' ? '' : $env{'form.kwstyle'}; (my $styleoff = $styleon) =~ s/\$styleon$_$styleoff<\/font>/gi; + foreach my $keyword (@keylist) { + $string =~ s/\b\Q$keyword\E(\b|\.)/$styleon$keyword$styleoff<\/font>/gi; } return $string; } @@ -2006,8 +2535,8 @@ sub keywords_highlight { #--- Called from submission routine sub processHandGrade { my ($request) = shift; - my $url = $env{'form.url'}; - my $symb = $env{'form.symb'}; + my $symb = &get_symb($request); + my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb); my $button = $env{'form.gradeOpt'}; my $ngrade = $env{'form.NCT'}; my $ntstu = $env{'form.NTSTU'}; @@ -2018,21 +2547,26 @@ sub processHandGrade { my $ctr = 0; while ($ctr < $ngrade) { my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr}); - my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr); + my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$symb,$uname,$udom,$ctr); if ($errorflag eq 'no_score') { $ctr++; next; } if ($errorflag eq 'not_allowed') { - $request->print("Not allowed to modify grades for $uname:$udom"); + $request->print("Not allowed to modify grades for $uname:$udom"); $ctr++; next; } my $includemsg = $env{'form.includemsg'.$ctr}; my ($subject,$message,$msgstatus) = ('','',''); + my $restitle = &Apache::lonnet::gettitle($symb); + my ($feedurl,$showsymb) = + &get_feedurl_and_symb($symb,$uname,$udom); + my $messagetail; if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) { $subject = $env{'form.msgsub'} if ($includemsg =~ /msgsub/); unless ($subject=~/\w/) { $subject=&mt('Grading Feedback'); } + $subject.=' ['.$restitle.']'; my (@msgnum) = split(/,/,$includemsg); foreach (@msgnum) { $message.=$env{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne ''); @@ -2040,14 +2574,16 @@ sub processHandGrade { $message =&Apache::lonfeedback::clear_out_html($message); if ($env{'form.withgrades'.$ctr}) { $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt; - $message.=" for $env{'form.probTitle'}"; - } - $msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom, - $subject.' ['. - &Apache::lonnet::declutter($url).']',$message); - $request->print('
'.&mt('Sending message to [_1]@[_2]',$uname,$udom).': '. + $messagetail = " for $env{'form.probTitle'}"; + } + $msgstatus = + &Apache::lonmsg::user_normal_msg($uname,$udom,$subject, + $message.$messagetail, + undef,$feedurl,undef, + undef,undef,$showsymb, + $restitle); + $request->print('
'.&mt('Sending message to [_1]',$uname.':'.$udom).': '. $msgstatus); } if ($env{'form.collaborator'.$ctr}) { @@ -2056,15 +2592,21 @@ sub processHandGrade { my ($part,@collaborators) = split(/:/,$collabstr); foreach my $collaborator (@collaborators) { my ($errorflag,$pts,$wgt) = - &saveHandGrade($request,$url,$symb,$collaborator,$udom,$ctr, + &saveHandGrade($request,$symb,$collaborator,$udom,$ctr, $env{'form.unamedom'.$ctr},$part); if ($errorflag eq 'not_allowed') { - $request->print("Not allowed to modify grades for $collaborator:$udom"); + $request->print("".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom").""); next; - } else { - if ($message ne '') { - $msgstatus = &Apache::lonmsg::user_normal_msg($collaborator,$udom,$env{'form.msgsub'},$message); + } elsif ($message ne '') { + my ($baseurl,$showsymb) = + &get_feedurl_and_symb($symb,$collaborator, + $udom); + if ($env{'form.withgrades'.$ctr}) { + $messagetail = " for $env{'form.probTitle'}"; } + $msgstatus = + &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle); } } } @@ -2134,7 +2676,7 @@ sub processHandGrade { # Go directly to grade student - from submission or link from chart page if ($button eq 'Grade Student') { - (undef,undef,$env{'form.handgrade'},undef,undef) = &showResourceInfo($url); +# (undef,undef,$env{'form.handgrade'},undef,undef) = &showResourceInfo($symb); my $processUser = $env{'form.unamedom'.$env{'form.studentNo'}}; ($env{'form.student'},$env{'form.userdom'}) = split(/:/,$processUser); $env{'form.fullname'} = $$fullname{$processUser}; @@ -2154,7 +2696,7 @@ sub processHandGrade { my (@parsedlist,@nextlist); my ($nextflg) = 0; - foreach (sort + foreach my $item (sort { if (lc($$fullname{$a}) ne lc($$fullname{$b})) { return (lc($$fullname{$a}) cmp lc($$fullname{$b})); @@ -2162,17 +2704,22 @@ sub processHandGrade { return $a cmp $b; } (keys(%$fullname))) { if ($nextflg == 1 && $button =~ /Next$/) { - push @parsedlist,$_; + push(@parsedlist,$item); } - $nextflg = 1 if ($_ eq $laststu); + $nextflg = 1 if ($item eq $laststu); if ($button eq 'Previous') { - last if ($_ eq $firststu); - push @parsedlist,$_; + last if ($item eq $firststu); + push(@parsedlist,$item); } } $ctr = 0; @parsedlist = reverse @parsedlist if ($button eq 'Previous'); - my ($partlist) = &response_type($url); + my $res_error; + my ($partlist) = &response_type($symb,\$res_error); + if ($res_error) { + $request->print(&navmap_errormsg()); + return; + } foreach my $student (@parsedlist) { my $submitonly=$env{'form.submitonly'}; my ($uname,$udom) = split(/:/,$student); @@ -2186,15 +2733,15 @@ sub processHandGrade { if ($submitonly =~ /^(yes|graded|incorrect)$/) { # my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname); - my %status=&student_gradeStatus($url,$symb,$udom,$uname,$partlist); + my %status=&student_gradeStatus($symb,$udom,$uname,$partlist); my $submitted = 0; my $ungraded = 0; my $incorrect = 0; - foreach (keys(%status)) { - $submitted = 1 if ($status{$_} ne 'nothing'); - $ungraded = 1 if ($status{$_} =~ /^ungraded/); - $incorrect = 1 if ($status{$_} =~ /^incorrect/); - my ($foo,$partid,$foo1) = split(/\./,$_); + foreach my $item (keys(%status)) { + $submitted = 1 if ($status{$item} ne 'nothing'); + $ungraded = 1 if ($status{$item} =~ /^ungraded/); + $incorrect = 1 if ($status{$item} =~ /^incorrect/); + my ($foo,$partid,$foo1) = split(/\./,$item); if ($status{'resource.'.$partid.'.submitted_by'} ne '') { $submitted = 0; } @@ -2205,7 +2752,7 @@ sub processHandGrade { next if (!$ungraded && ($submitonly eq 'graded')); next if (!$incorrect && $submitonly eq 'incorrect'); } - push @nextlist,$student if ($ctr < $ntstu); + push(@nextlist,$student) if ($ctr < $ntstu); last if ($ctr == $ntstu); $ctr++; } @@ -2213,7 +2760,7 @@ sub processHandGrade { $ctr = 0; my $total = scalar(@nextlist)-1; - foreach (sort @nextlist) { + foreach (sort(@nextlist)) { my ($uname,$udom,$submitter) = split(/:/); $env{'form.student'} = $uname; $env{'form.userdom'} = $udom; @@ -2222,10 +2769,10 @@ sub processHandGrade { $ctr++; } if ($total < 0) { - my $the_end = '

LON-CAPA User Message


'."\n"; - $the_end.='Message: No more students for this section or class.

'."\n"; - $the_end.='Click on the button below to return to the grading menu.

'."\n"; - $the_end.=&show_grading_menu_form($symb,$url); + my $the_end = '

'.&mt('LON-CAPA User Message').'


'."\n"; + $the_end.=&mt('Message: No more students for this section or class.').'

'."\n"; + $the_end.=&mt('Click on the button below to return to the grading menu.').'

'."\n"; + $the_end.=&show_grading_menu_form($symb); $request->print($the_end); } return ''; @@ -2233,21 +2780,20 @@ sub processHandGrade { #---- Save the score and award for each student, if changed sub saveHandGrade { - my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_; - my @v_flag; + my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_; + my @version_parts; my $usec = &Apache::lonnet::getsection($domain,$stuname, $env{'request.course.id'}); if (!&canmodify($usec)) { return('not_allowed'); } - my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname); + my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname); my @parts_graded; my %newrecord = (); my ($pts,$wgt) = ('',''); my %aggregate = (); my $aggregateflag = 0; - my @parts = split(/:/,$env{'form.partlist'.$newflg}); foreach my $new_part (@parts) { - #collaborator may vary for different parts + #collaborator ($submi may vary for different parts if ($submitter && $new_part ne $part) { next; } my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$new_part}; if ($dropMenu eq 'excused') { @@ -2256,11 +2802,11 @@ sub saveHandGrade { if (exists($record{'resource.'.$new_part.'.awarded'})) { $newrecord{'resource.'.$new_part.'.awarded'} = ''; } - $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}"; + $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}"; } } elsif ($dropMenu eq 'reset status' && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts - foreach my $key (keys (%record)) { + foreach my $key (keys(%record)) { if ($key=~/^resource\.\Q$new_part\E\./) { $newrecord{$key} = ''; } } $newrecord{'resource.'.$new_part.'.regrader'}= @@ -2277,7 +2823,7 @@ sub saveHandGrade { my $solvedstatus = $record{'resource.'.$new_part.'.solved'}; if ($aggtries > 0) { - &decrement($symb,$new_part,\%aggregate,$aggtries,$totaltries,$solvedstatus); + &decrement_aggs($symb,$new_part,\%aggregate,$aggtries,$totaltries,$solvedstatus); $aggregateflag = 1; } } elsif ($dropMenu eq '') { @@ -2292,9 +2838,10 @@ sub saveHandGrade { my $partial= $pts/$wgt; if ($partial eq $record{'resource.'.$new_part.'.awarded'}) { #do not update score for part if not changed. + &handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord); next; } else { - push @parts_graded, $new_part; + push(@parts_graded,$new_part); } if ($record{'resource.'.$new_part.'.awarded'} ne $partial) { $newrecord{'resource.'.$new_part.'.awarded'} = $partial; @@ -2316,57 +2863,31 @@ sub saveHandGrade { $newrecord{'resource.'.$new_part.'.regrader'}= "$env{'user.name'}:$env{'user.domain'}"; } - if ($env{'form.part'.$new_part.'_returndoc1'}) { - # if multiple files are uploaded names will be 'returndoc2','returndoc3' - - my $portfolio_root = &Apache::loncommon::propath($domain, - $stuname). - '/userfiles/portfolio'; - $request->print('
'.$portfolio_root.'
'); - - # my $result=&Apache::lonnet::userfileupload('uploaddoc','', - # 'portfolio'.$env{'form.currentpath'}); - - my $file_counter = 1; - my $respid = $env{'form.respid'}; - while ($env{'form.part'.$new_part.'_returndoc'.$file_counter}) { - my $fname=$env{'form.returndoc'.$file_counter.'.filename'}; - $newrecord{"resource.$new_part.$respid.handback"} = $env{'form.returndocorig'.$file_counter}; - $request->print("
".$fname." will be the uploaded file name"); - $request->print("Will upload document".$env{'form.returndocorig'.$file_counter}); - $file_counter++; - } - } - # unless problem has been graded, set flag to version the submitted files unless ($record{'resource.'.$new_part.'.solved'} =~ /^correct_/ || $record{'resource.'.$new_part.'.solved'} eq 'incorrect_by_override' || $dropMenu eq 'reset status') { - push (@v_flag,$new_part); + push(@version_parts,$new_part); } } my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - if (scalar(keys(%newrecord)) > 0) { - if (scalar(@v_flag)) { - &version_portfiles(\%record, \@parts_graded, $env{'request.course.id'}, $symb, $domain, $stuname, \@v_flag); + if (%newrecord) { + if (@version_parts) { + my @changed_keys = &version_portfiles(\%record, \@parts_graded, + $env{'request.course.id'}, $symb, $domain, $stuname, \@version_parts); + @newrecord{@changed_keys} = @record{@changed_keys}; + foreach my $new_part (@version_parts) { + &handback_files($request,$symb,$stuname,$domain,$newflg, + $new_part,\%newrecord); + } } &Apache::lonnet::cstore(\%newrecord,$symb, $env{'request.course.id'},$domain,$stuname); - - my @ungraded_parts; - foreach my $part (@parts) { - if ( !defined($record{'resource.'.$part.'.awarded'}) - && !defined($newrecord{'resource.'.$part.'.awarded'}) ) { - push(@ungraded_parts, $part); - } - } - if ( !@ungraded_parts ) { - &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,$cdom, - $cnum,$domain,$stuname); - } + &check_and_remove_from_queue(\@parts,\%record,\%newrecord,$symb, + $cdom,$cnum,$domain,$stuname); } if ($aggregateflag) { &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate, @@ -2374,6 +2895,108 @@ sub saveHandGrade { } return ('',$pts,$wgt); } + +sub check_and_remove_from_queue { + my ($parts,$record,$newrecord,$symb,$cdom,$cnum,$domain,$stuname) = @_; + my @ungraded_parts; + foreach my $part (@{$parts}) { + if ( $record->{ 'resource.'.$part.'.awarded'} eq '' + && $record->{ 'resource.'.$part.'.solved' } ne 'excused' + && $newrecord->{'resource.'.$part.'.awarded'} eq '' + && $newrecord->{'resource.'.$part.'.solved' } ne 'excused' + ) { + push(@ungraded_parts, $part); + } + } + if ( !@ungraded_parts ) { + &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,$cdom, + $cnum,$domain,$stuname); + } +} + +sub handback_files { + my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_; + my $portfolio_root = '/userfiles/portfolio'; + my $res_error; + my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error); + if ($res_error) { + $request->print('
'.&navmap_errormsg().'
'); + return; + } + my @part_response_id = &flatten_responseType($responseType); + foreach my $part_response_id (@part_response_id) { + my ($part_id,$resp_id) = @{ $part_response_id }; + my $part_resp = join('_',@{ $part_response_id }); + if (($env{'form.'.$newflg.'_'.$part_resp.'_returndoc1'}) && ($new_part == $part_id)) { + # if multiple files are uploaded names will be 'returndoc2','returndoc3' + my $file_counter = 1; + my $file_msg; + while ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter}) { + my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'.filename'}; + my ($directory,$answer_file) = + ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter} =~ /^(.*?)([^\/]*)$/); + my ($answer_name,$answer_ver,$answer_ext) = + &file_name_version_ext($answer_file); + my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/); + my $getpropath = 1; + my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$portfolio_path,$domain,$stuname,$getpropath); + my $version = &get_next_version($answer_name, $answer_ext, \@dir_list); + # fix file name + my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/); + my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain, + $newflg.'_'.$part_resp.'_returndoc'.$file_counter, + $save_file_name); + if ($result !~ m|^/uploaded/|) { + $request->print('
'. + &mt('An error occurred ([_1]) while trying to upload [_2].', + $result,$newflg.'_'.$part_resp.'_returndoc'.$file_counter). + ''); + } else { + # mark the file as read only + my @files = ($save_file_name); + my @what = ($symb,$env{'request.course.id'},'handback'); + &Apache::lonnet::mark_as_readonly($domain,$stuname,\@files,\@what); + if (exists($$newrecord{"resource.$new_part.$resp_id.handback"})) { + $$newrecord{"resource.$new_part.$resp_id.handback"}.=','; + } + $$newrecord{"resource.$new_part.$resp_id.handback"} .= $save_file_name; + $file_msg.= "\n".'
'.$save_file_name."
"; + + } + $request->print("
".$fname." will be the uploaded file name"); + $request->print(" ".$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter}); + $file_counter++; + } + my $subject = "File Handed Back by Instructor "; + my $message = "A file has been returned that was originally submitted in reponse to:
"; + $message .= "".&Apache::lonnet::gettitle($symb)."
"; + $message .= ' The returned file(s) are named: '. $file_msg; + $message .= " and can be found in your portfolio space."; + my ($feedurl,$showsymb) = + &get_feedurl_and_symb($symb,$domain,$stuname); + my $restitle = &Apache::lonnet::gettitle($symb); + my $msgstatus = + &Apache::lonmsg::user_normal_msg($stuname,$domain,$subject. + ' (File Returned) ['.$restitle.']',$message,undef, + $feedurl,undef,undef,undef,$showsymb,$restitle); + } + } + return; +} + +sub get_feedurl_and_symb { + my ($symb,$uname,$udom) = @_; + my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb); + $url = &Apache::lonnet::clutter($url); + my $encrypturl=&Apache::lonnet::EXT('resource.0.encrypturl', + $symb,$udom,$uname); + if ($encrypturl =~ /^yes$/i) { + &Apache::lonenc::encrypted(\$url,1); + &Apache::lonenc::encrypted(\$symb,1); + } + return ($url,$symb); +} + sub get_submitted_files { my ($udom,$uname,$partid,$respid,$record) = @_; my @files; @@ -2388,6 +3011,7 @@ sub get_submitted_files { } return (\@files); } + # ----------- Provides number of tries since last reset. sub get_num_tries { my ($record,$last_reset,$part) = @_; @@ -2423,7 +3047,7 @@ sub decrement_aggs { if ($aggtries == $totaltries) { $decrement{'users'} = 1; } - foreach my $type (keys (%decrement)) { + foreach my $type (keys(%decrement)) { $$aggregate{$symb."\0".$part."\0".$type} = -$decrement{$type}; } return; @@ -2451,40 +3075,39 @@ sub get_last_resets { sub version_portfiles { my ($record, $parts_graded, $courseid, $symb, $domain, $stu_name, $v_flag) = @_; my $version_parts = join('|',@$v_flag); + my @returned_keys; my $parts = join('|', @$parts_graded); - my $portfolio_root = &Apache::loncommon::propath($domain, - $stu_name). - '/userfiles/portfolio'; + my $portfolio_root = '/userfiles/portfolio'; foreach my $key (keys(%$record)) { my $new_portfiles; if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) { - my @v_portfiles; - my @portfiles = split(/,/,$$record{$key}); + my @versioned_portfiles; + my @portfiles = split(/\s*,\s*/,$$record{$key}); foreach my $file (@portfiles) { &Apache::lonnet::unmark_as_readonly($domain,$stu_name,[$symb,$env{'request.course.id'}],$file); my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/); - my $version = 0; my ($answer_name,$answer_ver,$answer_ext) = &file_name_version_ext($answer_file); - my @dir_list = &Apache::lonnet::dirlist($directory,$domain,$stu_name,$portfolio_root); - $version = &get_next_version($answer_name, $answer_ext, \@dir_list); + my $getpropath = 1; + my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$directory,$domain,$stu_name,$getpropath); + my $version = &get_next_version($answer_name, $answer_ext, \@dir_list); my $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version); if ($new_answer ne 'problem getting file') { - push(@v_portfiles, $directory.$new_answer); + push(@versioned_portfiles, $directory.$new_answer); &Apache::lonnet::mark_as_readonly($domain,$stu_name, - ['/portfolio'.$directory.$new_answer], + [$directory.$new_answer], [$symb,$env{'request.course.id'},'graded']); } - } - $$record{$key} = join(',',@v_portfiles); + $$record{$key} = join(',',@versioned_portfiles); + push(@returned_keys,$key); } } - return 'ok'; + return (@returned_keys); } sub get_next_version { - my ($answer_name, $answer_ext, $dir_list); + my ($answer_name, $answer_ext, $dir_list) = @_; my $version; foreach my $row (@$dir_list) { my ($file) = split(/\&/,$row,2); @@ -2512,7 +3135,6 @@ sub version_selected_portfile { my $new_answer; $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stu_name/portfolio$directory$file_name"); if($env{'form.copy'} eq '-1') { - &Apache::lonnet::logthis('problem getting file '.$file_name); $new_answer = 'problem getting file'; } else { $new_answer = $answer_name.'.'.$version.'.'.$answer_ext; @@ -2547,15 +3169,15 @@ sub file_name_version_ext { sub viewgrades_js { my ($request) = shift; - $request->print(< + my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = '); + $request->print(&Apache::lonhtmlcommon::scripttag(< VIEWJAVASCRIPT } @@ -2718,7 +3339,7 @@ sub viewgrades { my ($request) = shift; &viewgrades_js($request); - my ($symb,$url) = ($env{'form.symb'},$env{'form.url'}); + my ($symb) = &get_symb($request); #need to make sure we have the correct data for later EXT calls, #thus invalidate the cache &Apache::lonnet::devalidatecourseresdata( @@ -2726,108 +3347,134 @@ sub viewgrades { $env{'course.'.$env{'request.course.id'}.'.domain'}); &Apache::lonnet::clear_EXT_cache_status(); - my $result='

'.&mt('Manual Grading').'

'; - $result.='Current Resource: '.$env{'form.probTitle'}.''."\n"; + my $result='

'.&mt('Manual Grading').'

'; + $result.='

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

'."\n"; #view individual student submission form - called using Javascript viewOneStudent - $result.=&jscriptNform($url,$symb); + $result.=&jscriptNform($symb); #beginning of class grading form + my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status')); $result.= '
'."\n". - ''."\n". - ''."\n". + ''."\n". ''."\n". - ''."\n". + &build_section_inputs(). ''."\n". - ''."\n". + ''."\n". ''."\n"; - my $sectionClass; + my ($common_header,$specific_header); if ($env{'form.section'} eq 'all') { - $sectionClass='Class '; + $common_header = &mt('Assign Common Grade to Class'); + $specific_header = &mt('Assign Grade to Specific Students in Class'); } elsif ($env{'form.section'} eq 'none') { - $sectionClass='Students in no Section '; + $common_header = &mt('Assign Common Grade to Students in no Section'); + $specific_header = &mt('Assign Grade to Specific Students in no Section'); } else { - $sectionClass='Students in Section '.$env{'form.section'}.''; + my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section')); + $common_header = &mt('Assign Common Grade to Students in Section(s) [_1]',$section_display); + $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1]',$section_display); } - $result.='

Assign Common Grade To '.$sectionClass; - $result.= '
'."\n". - '
'; + $result.= '

'.$common_header.'

'.&Apache::loncommon::start_data_table(); #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 $res_error; + my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error); + if ($res_error) { + return &navmap_errormsg(); + } my %weight = (); my $ctsparts = 0; - $result.=''; my %seen = (); - for (sort keys(%$handgrade)) { - my ($partid,$respid) = split (/_/,$_,2); + my @part_response_id = &flatten_responseType($responseType); + 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}; $seen{$partid}++; - my $handgrade=$$handgrade{$_}; + my $handgrade=$$handgrade{$part_resp}; my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb); $weight{$partid} = $wgt eq '' ? '1' : $wgt; - $result.=''."\n"; - $result.=''."\n"; - my $display_part=&get_display_part($partid,$url,$symb); - $result.=''."\n"; - $result.= ''. - ''."\n"; + ''. + ''. + ''. + ''. + ''. + &Apache::loncommon::end_data_table_row()."\n"; $ctsparts++; } - $result.='
Part: '.$display_part.'   Point: '; - $result.=''; + my $display_part=&get_display_part($partid,$symb); + my $radio.='
'; my $ctr = 0; while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across - $result.= '\n"; $result.=(($ctr+1)%10 == 0 ? '' : ''); $ctr++; } - $result.='
'; - $result.= '
or /'. - $weight{$partid}.' (problem weight) '. - ''. - ''. - '
'; + $line.=''."\n"; + $line.=''."\n"; + + $result.= + &Apache::loncommon::start_data_table_row()."\n". + ''.&mt('Part:').''.$display_part.''.&mt('Points:').''.$radio.''.&mt('or').''.$line.'
'.'
'.'
'."\n". + $result.=&Apache::loncommon::end_data_table()."\n". ''; - $result.=''; + $result.=''; #table listing all the students in a section/class #header of table - $result.= '

Assign Grade to Specific Students in '.$sectionClass; - $result.= '
'."\n". - ''. - '\n"; - my (@parts) = sort(&getpartlist($url,$symb)); + $result.= '

'.$specific_header.'

'. + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(). + ''. + '\n"; + my $partserror; + my (@parts) = sort(&getpartlist($symb,\$partserror)); + if ($partserror) { + return &navmap_errormsg(); + } + my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb); my @partids = (); foreach my $part (@parts) { my $display=&Apache::lonnet::metadata($url,$part.'.display'); - $display =~ s|^Number of Attempts|Tries
|; # makes the column narrower + my $narrowtext = &mt('Tries'); + $display =~ s|^Number of Attempts|$narrowtext
|; # makes the column narrower if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); } my ($partid) = &split_part_type($part); - push(@partids, $partid); - my $display_part=&get_display_part($partid,$url,$symb); + push(@partids,$partid); + my $display_part=&get_display_part($partid,$symb); if ($display =~ /^Partial Credit Factor/) { - $result.=''."\n"; + $result.=''."\n"; next; + } else { - $display =~s/\[Part: \Q$partid\E\]/Part:<\/b> $display_part/; + if ($display =~ /Problem Status/) { + my $grade_status_mt = &mt('Grade Status'); + $display =~ s{Problem Status}{$grade_status_mt
}; + } + my $part_mt = &mt('Part:'); + $display =~s{\[Part: \Q$partid\E\]}{$part_mt $display_part}; } - $display =~ s|Problem Status|Grade Status
|; - $result.=''."\n"; + + $result.=''."\n"; } - $result.=''; + $result.=&Apache::loncommon::end_data_table_header_row(); my %last_resets = &get_last_resets($symb,$env{'request.course.id'},\@partids); @@ -2844,34 +3491,38 @@ sub viewgrades { return $a cmp $b; } (keys(%$fullname))) { $ctr++; - $result.=&viewstudentgrade($url,$symb,$env{'request.course.id'}, + $result.=&viewstudentgrade($symb,$env{'request.course.id'}, $_,$$fullname{$_},\@parts,\%weight,$ctr,\%last_resets); } - $result.='
 No. '.&nameUserString('header')."'.&mt('No.').''.&nameUserString('header')."Score Part: '.$display_part. - '
(weight = '.$weight{$partid}.')
'. + &mt('Score Part: [_1]
(weight = [_2])', + $display_part,$weight{$partid}).'
'.$display.''.$display.'
'; + $result.=&Apache::loncommon::end_data_table(); $result.=''."\n"; - $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.'; + my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section')); + my $stu_status = join(' or ',&Apache::loncommon::get_env_multiple('form.Status')); + $result=''. + &mt('There are no students in section(s) [_1] with enrollment status [_2] to modify or grade.', + $section_display, $stu_status). + ''; } - $result.=&show_grading_menu_form($symb,$url); + $result.=&show_grading_menu_form($symb); return $result; } #--- call by previous routine to display each student sub viewstudentgrade { - my ($url,$symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_; + my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_; my ($uname,$udom) = split(/:/,$student); my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname); my %aggregates = (); - my $result=''. + my $result=&Apache::loncommon::start_data_table_row().''. ''. "\n".$ctr.'  '. ''.$fullname.' '. - '('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')'."\n"; + '\');" target="_self">'.$fullname.' '. + '('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')'."\n"; $student=~s/:/_/; # colon doen't work in javascript for names foreach my $apart (@$parts) { my ($part,$type) = &split_part_type($apart); @@ -2898,7 +3549,7 @@ sub viewstudentgrade { 'GD_'.$student.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n"; $result.=''."\n"; } elsif ($type eq 'solved') { my ($status,$foo)=split(/_/,$score,2); @@ -2907,10 +3558,10 @@ sub viewstudentgrade { $part.'_solved_s" value="'.$status.'" />'."\n"; $result.='  \n"; } else { $result.=''."\n"; } } - $result.=''; + $result.=&Apache::loncommon::end_data_table_row(); return $result; } @@ -2930,22 +3581,22 @@ sub viewstudentgrade { sub editgrades { my ($request) = @_; - my $symb=$env{'form.symb'}; - my $url =$env{'form.url'}; - my $title='

Current Grade Status

'; - $title.='Current Resource: '.$env{'form.probTitle'}.'
'."\n"; - $title.='Section: '.$env{'form.section'}.''."\n"; - - my $result= '
'."\n"; - $result.= ''. - ''. - '\n"; - + my $symb=&get_symb($request); + my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section')); + my $title='

'.&mt('Current Grade Status').'

'; + $title.='

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

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

'.&mt('Section: [_1]',$section_display).'

'."\n"; + + my $result= &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(). + ''. + '\n"; my %scoreptr = ( 'correct' =>'correct_by_override', 'incorrect'=>'incorrect_by_override', 'excused' =>'excused', 'ungraded' =>'ungraded_attempted', + 'credited' =>'credit_attempted', 'nothing' => '', ); my ($classlist,undef,$fullname) = &getclasslist($env{'form.section'},'0'); @@ -2955,41 +3606,48 @@ sub editgrades { my %columns = (); my ($i,$ctr,$count,$rec_update) = (0,0,0,0); - my (@parts) = sort(&getpartlist($url,$symb)); + my $partserror; + my (@parts) = sort(&getpartlist($symb,\$partserror)); + if ($partserror) { + return &navmap_errormsg(); + } my $header; while ($ctr < $env{'form.totalparts'}) { my $partid = $env{'form.partid_'.$ctr}; - push @partid,$partid; + push(@partid,$partid); $weight{$partid} = $env{'form.weight_'.$partid}; $ctr++; } + my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb); foreach my $partid (@partid) { - $header .= ''. - ''; + $header .= ''. + ''; $columns{$partid}=2; foreach my $stores (@parts) { my ($part,$type) = &split_part_type($stores); if ($part !~ m/^\Q$partid\E/) { next;} if ($type eq 'awarded' || $type eq 'solved') { next; } my $display=&Apache::lonnet::metadata($url,$stores.'.display'); - $display =~ s/\[Part: (\w)+\]//; - $display =~ s/Number of Attempts/Tries/; - $header .= ''. - ''; + $display =~ s/\[Part: \Q$part\E\]//; + my $narrowtext = &mt('Tries'); + $display =~ s/Number of Attempts/$narrowtext/; + $header .= ''. + ''; $columns{$partid}+=2; } } foreach my $partid (@partid) { - my $display_part=&get_display_part($partid,$url,$symb); - $result .= ''; + my $display_part=&get_display_part($partid,$symb); + $result .= ''; } - $result .= ''; - $result .= $header; - $result .= ''."\n"; - my $noupdate; + $result .= &Apache::loncommon::end_data_table_header_row(). + &Apache::loncommon::start_data_table_header_row(). + $header. + &Apache::loncommon::end_data_table_header_row(); + my @noupdate; my ($updateCtr,$noupdateCtr) = (1,1); for ($i=0; $i<$env{'form.total'}; $i++) { my $line; @@ -3001,7 +3659,9 @@ sub editgrades { my $usec=$classlist->{"$uname:$udom"}[5]; if (!&canmodify($usec)) { my $numcols=scalar(@partid)*4+2; - $noupdate.=$line.""; + push(@noupdate, + $line.""); next; } my %aggregate = (); @@ -3070,7 +3730,7 @@ sub editgrades { ''; } } - $line.=''."\n"; + $line.="\n"; my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; @@ -3103,10 +3763,13 @@ sub editgrades { } } - $result.=''.$line; + $result.=&Apache::loncommon::start_data_table_row(). + ''.$line. + &Apache::loncommon::end_data_table_row(); $updateCtr++; } else { - $noupdate.=''.$line; + push(@noupdate, + ''.$line); $noupdateCtr++; } if ($aggregateflag) { @@ -3114,16 +3777,28 @@ sub editgrades { $cdom,$cnum); } } - if ($noupdate) { + if (@noupdate) { # my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3; my $numcols=scalar(@partid)*4+2; - $result .= ''.$noupdate; - } - $result .= '
 No. '.&nameUserString('header')."'.&mt('No.').''.&nameUserString('header')." Old Score  New Score '.&mt('Old Score').''.&mt('New Score').' Old '.$display.'  New '.$display.' '.&mt('Old').' '.$display.''.&mt('New').' '.$display.'Part: '.$display_part. - ' (Weight = '.$weight{$partid}.')'. + &mt('Part: [_1] (Weight = [_2])',$display_part,$weight{$partid}). + '
Not allowed to modify student
". + &mt('Not allowed to modify student')."
'.$awarded.' 
 '.$updateCtr.'  '.$updateCtr.' 
 '.$noupdateCtr.'  '.$noupdateCtr.' 
No Changes Occurred For the Students Below
'."\n". - &show_grading_menu_form ($symb,$url); - my $msg = '
Number of records updated = '.$rec_update. - ' for '.$count.' student'.($count <= 1 ? '' : 's').'.
'. - 'Total number of students = '.$env{'form.total'}.'
'; + $result .= &Apache::loncommon::start_data_table_row('LC_empty_row'). + ''. + &mt('No Changes Occurred For the Students Below'). + ''. + &Apache::loncommon::end_data_table_row(); + foreach my $line (@noupdate) { + $result.= + &Apache::loncommon::start_data_table_row(). + $line. + &Apache::loncommon::end_data_table_row(); + } + } + $result .= &Apache::loncommon::end_data_table(). + &show_grading_menu_form($symb); + my $msg = '

'. + &mt('Number of records updated = [_1] for [quant,_2,student].', + $rec_update,$count).'
'. + ''.&mt('Total number of students = [_1]',$env{'form.total'}). + '

'; return $title.$msg.$result; } @@ -3131,7 +3806,7 @@ sub split_part_type { my ($partstr) = @_; my ($temp,@allparts)=split(/_/,$partstr); my $type=pop(@allparts); - my $part=join('.',@allparts); + my $part=join('_',@allparts); return ($part,$type); } @@ -3146,7 +3821,7 @@ sub split_part_type { # #--- Javascript to handle csv upload sub csvupload_javascript_reverse_associate { - my $error1=&mt('You need to specify the username or ID'); + my $error1=&mt('You need to specify the username or the student/employee ID'); my $error2=&mt('You need to specify at least one grading field'); return(<print(< -

Uploading Class Grades

+

Uploading Class Grades

$result -
+

Identify fields

Total number of records found in file: $distotal
Enter as many fields as you can. The system will inform you and bring you back to this page if the data selected is insufficient to run your class.
- + @@ -3253,25 +3930,29 @@ to this page if the data selected is ins -
- ENDPICK + $request->print(&Apache::lonhtmlcommon::scripttag($javascript)); return ''; } sub csvupload_fields { - my ($url,$symb) = @_; - my (@parts) = &getpartlist($url,$symb); - my @fields=(['ID','Student ID'], + my ($symb,$errorref) = @_; + my (@parts) = &getpartlist($symb,$errorref); + if (ref($errorref)) { + if ($$errorref) { + return; + } + } + + my @fields=(['ID','Student/Employee ID'], ['username','Student Username'], ['domain','Student Domain']); + my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb); foreach my $part (sort(@parts)) { my @datum; my $display=&Apache::lonnet::metadata($url,$part.'.display'); @@ -3292,63 +3973,65 @@ sub csvuploadmap_footer { -
+
ENDPICK } sub checkforfile_js { - my $result =< + my $alertmsg = &mt('Please use the browse button to select a file from your local directory.'); + my $result = &Apache::lonhtmlcommon::scripttag(< CSVFORMJS return $result; } sub upcsvScores_form { my ($request) = shift; - my ($symb,$url)=&get_symb_and_url($request); + my ($symb)=&get_symb($request); if (!$symb) {return '';} my $result=&checkforfile_js(); $env{'form.probTitle'} = &Apache::lonnet::gettitle($symb); - my ($table) = &showResourceInfo($url,$env{'form.probTitle'}); - $result.=$table; - $result.='
'."\n"; - $result.=''."\n"; +# my ($table) = &showResourceInfo($symb,$env{'form.probTitle'}); +# $result.=$table; + $result.='
'."\n"; - $result.=' Specify a file containing the class scores for current resource'. - '.
'."\n"; + $result.=''."\n"; $result.='
'."\n"; + $result.=' '.&mt('Specify a file containing the class scores for current resource.'). + '
'."\n"; + my $upload=&mt("Upload Scores"); my $upfile_select=&Apache::loncommon::upfile_select_html(); my $ignore=&mt('Ignore First Line'); + $symb = &Apache::lonenc::check_encrypt($symb); $result.=< - $upfile_select -
+
ENDUPFORM - $result.='
'."\n"; + $result.=&Apache::loncommon::help_open_topic("Course_Convert_To_CSV", + &mt("How do I create a CSV file from a spreadsheet")) + .'
'."\n"; $result.='


'."\n"; - $result.=&show_grading_menu_form($symb,$url); + $result.=&show_grading_menu_form($symb); return $result; } sub csvuploadmap { my ($request)= @_; - my ($symb,$url)=&get_symb_and_url($request); + my ($symb)=&get_symb($request); if (!$symb) {return '';} my $datatoken; @@ -3360,11 +4043,15 @@ sub csvuploadmap { } my @records=&Apache::loncommon::upfile_record_sep(); if ($env{'form.noFirstLine'}) { shift(@records); } - &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1); + &csvuploadmap_header($request,$symb,$datatoken,$#records+1); my ($i,$keyfields); if (@records) { - my @fields=&csvupload_fields($url,$symb); - + my $fieldserror; + my @fields=&csvupload_fields($symb,\$fieldserror); + if ($fieldserror) { + $request->print(&navmap_errormsg()); + return; + } if ($env{'form.upfile_associate'} eq 'reverse') { &Apache::loncommon::csv_print_samples($request,\@records); $i=&Apache::loncommon::csv_print_select_table($request,\@records, @@ -3385,19 +4072,19 @@ sub csvuploadmap { } } &csvuploadmap_footer($request,$i,$keyfields); - $request->print(&show_grading_menu_form($symb,$url)); + $request->print(&show_grading_menu_form($symb)); return ''; } sub csvuploadoptions { my ($request)= @_; - my ($symb,$url)=&get_symb_and_url($request); + my ($symb)=&get_symb($request); my $checked=(($env{'form.noFirstLine'})?'1':'0'); my $ignore=&mt('Ignore First Line'); $request->print(< -

Uploading Class Grade Options

+

Uploading Class Grade Options