--- loncom/homework/structuretags.pm 2013/06/05 15:37:26 1.512.2.3 +++ loncom/homework/structuretags.pm 2014/11/24 02:36:26 1.524 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # definition of tags that give a structure to a document # -# $Id: structuretags.pm,v 1.512.2.3 2013/06/05 15:37:26 raeburn Exp $ +# $Id: structuretags.pm,v 1.524 2014/11/24 02:36:26 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -254,6 +254,55 @@ function image_response_click (which, e) input_element.value = click; img_element.src = '/adm/randomlabel.png?token='+token+'&clickdata='+click; } + +var submithandled = 0; +var keypresshandled = 0; + +$(document).ready(function(){ + + $(document).keypress(function(event){ + var keycode = (event.keyCode ? event.keyCode : event.which); + if ((keycode == '13') && (keypresshandled == 0)) { + if ( $( document.activeElement ).hasClass("LC_textline") ) { + keypresshandled = 1; + var idsArray = $( document.activeElement ).attr("id").split(/HWVAL_/); + if (idsArray.length) { + event.preventDefault(); + var itemsArray = idsArray[1].split(/_/); + var buttonId = idsArray[0]+'submit_'+itemsArray[0]; + $("#"+buttonId).trigger("click"); + } + } + } + }); + + $(document).delegate('form :submit', 'click', function( event ) { + if ( $( this ).hasClass( "LC_hwk_submit" ) ) { + var buttonId = this.id; + if (submithandled == 0) { + submithandled = 1; + $( "#msg_"+buttonId ).css({"display": "inline","background-color": "#87cefa", + "color": "black","padding": "2px"}) ; + if (( $(this.form).id == "LC_page" ) && ($('input[name="all_submit"]').length )) { + if (buttonId != "all_submit") { + $( ".LC_status_"+buttonId ).hide(); + if (( "#"+buttonId+"_pressed" ).length) { + $( "#"+buttonId+"_pressed" ).val( "1" ); + } + } + } else { + $( ".LC_status_"+buttonId ).hide(); + } + $(this.form).submit(); + $( ".LC_hwk_submit" ).prop( "disabled", true); + $( ".LC_textline" ).prop( "readonly", "readonly"); + event.preventDefault(); + return true; + } + } + }); +}); + // ]]> JS @@ -414,6 +463,8 @@ sub page_start { $body_args{'add_entries'} = \%add_entries; if ( $env{'request.state'} eq 'construct') { $body_args{'only_body'} = 1; + } elsif ($target eq 'web') { + $body_args{'print_suppress'} = 1; } } $body_args{'no_auto_mt_title'} = 1; @@ -637,7 +688,7 @@ sub problem_edit_header { '.&Apache::lonxml::message_location().' '. - '
'; + '' - .'' .'' - .'
'; } sub problem_edit_footer { @@ -710,7 +761,7 @@ sub problem_web_to_edit_header { ".&mt("Problem Type:")." ".&option('yes','problemstatus').&mt("Show Feedback")." - ".&option('no', 'problemstatus').&mt("Don't Show Incorect/Correct Feedback")." + ".&option('no', 'problemstatus').&mt("Don't Show Incorrect/Correct Feedback")." ".&option('no_feedback_ever', 'problemstatus').&mt("Don't Show Any Feedback")." @@ -846,12 +897,37 @@ sub initialize_storage { =item finalize_storage() - Stores away the result has to a student's environment - checks form.grade_ for specific values, other wises stores - to the running users environment - Will increment totals for attempts, students, and corrects - if running user has student role. - + Stores away the result hash to a student's environment; + checks form.grade_ for specific values, otherwise stores + to the running user's environment. + + &check_correctness_changes() is called in two circumstances + in which the results hash is to be stored permanently, for + grading triggered by a student's submission, where feedback on + correctness is to be provided to the student. + + 1. Immediately prior to storing the results hash + + To handle the case where a student's submission (and award) were + stored after history was retrieved in &initialize_storage(), e.g., + if a student submitted answers in quick succession (e.g., from + multiple tabs). &Apache::inputtags::hidealldata() is called for + any parts with out-of-order storage (i.e., correct then incorrect, + where awarded >= 1 when correct). + + 2. Immediately after storing the results hash + + To handle the case where lond on the student's homeserver returns + delay:N -- where N is the number of transactions between the last + retrieved in &initialize_storage() and the last stored immediately + before permanent storage of the current transaction via + lond::store_handler(). &Apache::grades::makehidden() is called + for any parts with out-of-order storage (i.e., correct then incorrect, + where awarded >= 1 when correct). + + Will call &store_aggregates() to increment totals for attempts, + students, and corrects, if running user has student role. + =cut @@ -872,8 +948,92 @@ sub finalize_storage { $namespace,'',$domain,$name); &Apache::lonxml::debug('Construct Store return message:'.$result); } else { + my ($laststore,$checkedparts,@parts,%postcorrect); + if (($env{'user.name'} eq $name) && ($env{'user.domain'} eq $domain) && + (!$Apache::lonhomework::scantronmode) && (!defined($env{'form.grade_symb'})) && + (!defined($env{'form.grade_courseid'}))) { + if ($Apache::lonhomework::history{'version'}) { + $laststore = $Apache::lonhomework::history{'version'}.'='. + $Apache::lonhomework::history{'timestamp'}; + } else { + $laststore = '0=0'; + } + my %record = &Apache::lonnet::restore($symb,$courseid,$domain,$name); + if ($record{'version'}) { + my ($newversion,$oldversion,$oldtimestamp); + if ($Apache::lonhomework::history{'version'}) { + $oldversion = $Apache::lonhomework::history{'version'}; + $oldtimestamp = $Apache::lonhomework::history{'timestamp'}; + } else { + $oldversion = 0; + $oldtimestamp = 0; + } + if ($record{'version'} > $oldversion) { + if ($record{'timestamp'} >= $oldtimestamp) { + $laststore = $record{'version'}.'='.$record{'timestamp'}; + $newversion = $record{'version'} + 1; + $checkedparts = 1; + foreach my $key (keys(%Apache::lonhomework::results)) { + if ($key =~ /^resource\.([^\.]+)\.solved$/) { + my $part = $1; + if ($Apache::lonhomework::results{$key} eq 'incorrect_attempted') { + push(@parts,$part); + } + } + } + if (@parts) { + my @parts_to_hide = &check_correctness_changes($symb,$courseid,$domain,$name, + \%record,\@parts,$newversion, + $oldversion); + if (@parts_to_hide) { + foreach my $part (@parts_to_hide) { + $postcorrect{$part} = 1; + &Apache::inputtags::hidealldata($part); + } + } + } + } + } + } + } $result=&Apache::lonnet::cstore(\%Apache::lonhomework::results, - $symb,$courseid,$domain,$name); + $symb,$courseid,$domain,$name,$laststore); + if ($result =~ /^delay\:(\d+)$/) { + my $numtrans = $1; + my ($oldversion) = split(/=/,$laststore); + if ($numtrans) { + my $newversion = $oldversion + 1 + $numtrans; + my @possparts; + if ($checkedparts) { + foreach my $part (@parts) { + unless ($postcorrect{$part}) { + push(@possparts,$part); + } + } + } else { + foreach my $key (keys(%Apache::lonhomework::results)) { + if ($key =~ /^resource\.([^\.]+)\.solved$/) { + my $part = $1; + unless ($postcorrect{$part}) { + if ($Apache::lonhomework::results{$key} eq 'incorrect_attempted') { + push(@possparts,$part); + } + } + } + } + } + if (@possparts) { + my %newrecord = &Apache::lonnet::restore($symb,$courseid,$domain,$name); + my @parts_to_hide = &check_correctness_changes($symb,$courseid,$domain,$name, + \%newrecord,\@possparts,$newversion, + $oldversion); + if (@parts_to_hide) { + my $partslist = join(',',@parts_to_hide); + &Apache::grades::makehidden($newversion,$partslist,\%newrecord,$symb,$domain,$name,1); + } + } + } + } &Apache::lonxml::debug('Store return message:'.$result); &store_aggregates($symb,$courseid); } @@ -885,6 +1045,62 @@ sub finalize_storage { =pod +=item check_correctness_changes() + + For all parts for which current results contain a solved status + of "incorrect_attempted", check if there was a transaction in which + solved was set to "correct_by_student" in the time since the last + transaction (retrieved when &initialize_storage() was called i.e., + when &start_problem() was called), unless: + (a) questiontype parameter is set to survey or anonymous survey (+/- credit) + (b) problemstatus is set to no or no_feedback_ever + If such a transaction exists, and did not occur after "reset status" + by a user with grading privileges, then the current transaction is an + example of an out-of-order transaction (i.e., incorrect occurring after + correct). Accordingly, the current transaction should be hidden. + +=cut + + +sub check_correctness_changes { + my ($symb,$courseid,$domain,$name,$record,$parts,$newversion,$oldversion) = @_; + my @parts_to_hide; + unless ((ref($record) eq 'HASH') && (ref($parts) eq 'ARRAY')) { + return @parts_to_hide; + } + if (@{$parts}) { + my $usec; + if (($env{'user.name'} eq $name) && ($env{'user.domain'} eq $domain) && + ($env{'request.course.id'} eq $courseid)) { + $usec = $env{'request.course.sec'}; + } else { + $usec = &Apache::lonnet::getsection($domain,$name,$courseid); + } + foreach my $id (@{$parts}) { + next if (($Apache::lonhomework::results{'resource.'.$id.'.type'} =~ /survey/) || + (&Apache::lonnet::EXT("resource.$id.problemstatus",$symb, + $domain,$name,$usec,undef,$courseid) =~ /^no/)); + my $reset; + for (my $i=$newversion-1; $i>=$oldversion; $i--) { + if (($record->{$i.':resource.'.$id.'.regrader'}) && + ($record->{$i.':resource.'.$id.'.tries'} eq '') && + ($record->{$i.':resource.'.$id.'.award'} eq '')) { + $reset = 1; + } elsif (($record->{$i.":resource.$id.solved"} eq 'correct_by_student') && + ($record->{$i.":resource.$id.awarded"} >= 1)) { + unless ($reset) { + push(@parts_to_hide,$id); + last; + } + } + } + } + } + return @parts_to_hide; +} + +=pod + item store_aggregates() Sends hash of values to be incremented in nohist_resourcetracker.db @@ -966,8 +1182,8 @@ sub checkout_msg { 'resource'=>'The resource needs to be checked out', 'id_expln'=>'As a resource gets checked out, a unique timestamped ID is given to it, and a permanent record is left in the system.', 'warning'=>'Checking out resources is subject to course policies, and may exclude future credit even if done erroneously.', - 'checkout'=>'Check out Exam for Viewing', - 'checkout?'=>'Check out Exam?'); + 'checkout'=>'Check out Bubblesheet Exam for Viewing', + 'checkout?'=>'Check out Bubblesheet Exam?'); my $uri = &Apache::lonenc::check_encrypt($env{'request.uri'}); return (<$lt{'resource'} @@ -975,7 +1191,7 @@ sub checkout_msg {

$lt{'warning'}

- +
ENDCHECKOUT } @@ -1009,7 +1225,7 @@ sub firstaccess_msg { $result .= (< - + ENDCHECKOUT return $result; @@ -1168,7 +1384,7 @@ sub start_problem { $target eq 'tex') { if ($env{'form.markaccess'}) { my @interval=&Apache::lonnet::EXT("resource.0.interval"); - &Apache::lonnet::set_first_access($interval[1]); + &Apache::lonnet::set_first_access($interval[1],$interval[0]); } ($status,$accessmsg,$slot_name,$slot) = @@ -2057,9 +2273,15 @@ sub end_while { } $return = &Apache::run::run($code,$safeeval); } - if ($error) { - &Apache::lonxml::error('
'.&mt('Code ran too long. It ran for more than').' '.$Apache::lonnet::perlvar{'lonScriptTimeout'}.' '.&mt('seconds occurred while running <while> on line').' '.$line.'
'); - } + if ($error) { + &Apache::lonxml::error( + '
'
+               .&mt('Code ran too long. It ran for more than [_1] seconds.',
+                        $Apache::lonnet::perlvar{'lonScriptTimeout'})
+               .&mt('This occurred while running <while> on line [_1].',
+                        $line)
+               .'
'); + } } elsif ($target eq "edit") { $result.= &Apache::edit::tag_end($target,$token,''); } @@ -2496,7 +2718,7 @@ sub start_problemtype { ['hide','Hide']] ,$token); $result .=&Apache::edit::checked_arg('When used as type(s):','for', - [ ['exam','Exam/Quiz Problem'], + [ ['exam','Bubblesheet Exam/Quiz Problem'], ['survey','Survey'], ['surveycred','Survey (with credit)'], ['anonsurvey','Anonymous Survey'], @@ -2543,13 +2765,13 @@ sub end_startouttext { .'
'.&mt('Delete?').' ' .&Apache::edit::deletelist($target,$token) .'' + .'' .&Apache::lonhtmlcommon::dragmath_button($areaid,1) .'' .&Apache::edit::insertlist($target,$token) .'' . + .'' . &Apache::loncommon::helpLatexCheatsheet(). &Apache::edit::end_row(). &Apache::edit::start_spanning_row()."\n".