--- loncom/homework/structuretags.pm 2013/06/05 15:37:26 1.512.2.3 +++ loncom/homework/structuretags.pm 2020/01/15 13:57:36 1.512.2.23.2.1 @@ -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.512.2.23.2.1 2020/01/15 13:57:36 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -224,9 +224,55 @@ sub end_tex { } sub homework_js { + my ($postsubmit,$timeout); + if (($env{'request.course.id'}) && ($env{'request.state'} ne 'construct')) { + my $crstype; + if (&Apache::loncommon::course_type() eq 'Community') { + $crstype = 'community'; + } else { + if ($env{'course.'.$env{'request.course.id'}.'.internal.coursecode'}) { + $crstype = 'official'; + } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.textbook'}) { + $crstype = 'textbook'; + } else { + $crstype = 'unofficial'; + } + } + $postsubmit = $env{'course.'.$env{'request.course.id'}.'.internal.postsubmit'}; + if ($postsubmit eq '') { + my %domdefs = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'}); + $postsubmit = $domdefs{'postsubmit'}; + unless ($postsubmit eq 'off') { + $timeout = $domdefs{$crstype.'postsubtimeout'}; + } + } elsif ($postsubmit eq '0') { + $postsubmit = 'off'; + } elsif ($postsubmit eq '1') { + $postsubmit = 'on'; + $timeout = $env{'course.'.$env{'request.course.id'}.'.internal.postsubtimeout'}; + if ($timeout eq '') { + my %domdefs = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'}); + $timeout = $domdefs{$crstype.'postsubtimeout'}; + } + } + if ($timeout eq '') { + $timeout = 60; + } + } else { + my %domdefs = &Apache::lonnet::get_domain_defaults($env{'request.role.domain'}); + $postsubmit = $domdefs{'postsubmit'}; + unless ($postsubmit eq 'off') { + $timeout = 60; + } + } + my $jstimeout = 0; + if ($timeout) { + $jstimeout = 1000 * $timeout; + } return &Apache::loncommon::resize_textarea_js(). + &Apache::loncommon::colorfuleditor_js(). &setmode_javascript(). - <<'JS'; + <<"JS"; JS @@ -264,8 +388,10 @@ sub setmode_javascript { @@ -291,13 +417,17 @@ sub page_start { $extra_head .= &homework_js(). &Apache::lonhtmlcommon::dragmath_js("EditMathPopup"); if (&Apache::lonhtmlcommon::htmlareabrowser()) { - my %textarea_args = ( + my %textarea_args; + if (($env{'request.state'} ne 'construct') || + ($env{'environment.nocodemirror'})) { + %textarea_args = ( dragmath => 'math', ); + } $extra_head .= &Apache::lonhtmlcommon::htmlareaselectactive(\%textarea_args); } my $is_task = ($env{'request.uri'} =~ /\.task$/); - my $needs_upload; + my ($needs_upload,$partlist); my ($symb)= &Apache::lonnet::whichuser(); my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb); if ($is_task) { @@ -315,6 +445,12 @@ sub page_start { unless ($is_page) { $needs_upload = 1; } + if ((ref($tagstack) eq 'ARRAY') && ($tagstack->[-1] eq 'problem')) { + my $res = $navmap->getBySymb($symb); + if (ref($res)) { + $partlist = $res->parts(); + } + } } } } else { @@ -326,10 +462,17 @@ sub page_start { if (ref($mapres)) { $is_page = $mapres->is_page(); } - unless ($is_page) { + if ($is_page) { + if ((ref($tagstack) eq 'ARRAY') && ($tagstack->[-1] eq 'problem')) { + my $res = $navmap->getBySymb($symb); + if (ref($res)) { + $partlist = $res->parts(); + } + } + } else { my $res = $navmap->getBySymb($symb); if (ref($res)) { - my $partlist = $res->parts(); + $partlist = $res->parts(); if (ref($partlist) eq 'ARRAY') { foreach my $part (@{$partlist}) { my @types = $res->responseType($part); @@ -351,7 +494,9 @@ sub page_start { } } if ($needs_upload) { - $extra_head .= &Apache::lonhtmlcommon::file_submissionchk_js(); + $extra_head .= &Apache::lonhtmlcommon::file_submissionchk_js(). + ''; } } @@ -370,6 +515,9 @@ sub page_start { "if (typeof swmenu != 'undefined') {swmenu.currentURL=null;}\n". &Apache::loncommon::browser_and_searcher_javascript(). "\n\n"; + if ($target eq 'edit') { + $extra_head .= &Apache::edit::js_update_linknum(); + } } } @@ -414,6 +562,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; @@ -460,7 +610,7 @@ sub page_start { "\t".''."\n"; } } - return ($page_start,$form_tag_start); + return ($page_start,$form_tag_start,$partlist); } #use Time::HiRes(); @@ -491,19 +641,45 @@ sub get_resource_name { } sub setup_rndseed { - my ($safeeval,$target)=@_; + my ($safeeval,$target,$probpartlist)=@_; my ($symb)=&Apache::lonnet::whichuser(); - my ($questiontype,$set_safespace,$rndseed); + my ($questiontype,$set_safespace,$rndseed,$numtries,$reqtries); if ($target eq 'analyze') { $questiontype = $env{'form.grade_questiontype'}; } unless (defined($questiontype)) { $questiontype = $Apache::lonhomework::type; } - if ($env{'request.state'} eq "construct" - || $symb eq '' - || $Apache::lonhomework::type eq 'practice' - || $Apache::lonhomework::history{'resource.CODE'}) { + if ($Apache::lonhomework::type eq 'randomizetry') { + my $partfortries = $Apache::inputtags::part; +# +# Where question type is "randomizetry" for a problem containing +# a single part (and unless type is explicitly set to not be +# "randomizetry" for that part), the number of tries used to +# determine randomization will be for that part, and randomization +# from calls to &random() in a perl script block before the part tag, +# will change based on the number of tries, and value of the +# "randomizeontries" parameter in effect for the single part. +# + if (ref($probpartlist) eq 'ARRAY') { + if ((@{$probpartlist} == 1) && ($probpartlist->[0] ne $partfortries)) { + if (&Apache::lonnet::EXT('resource.'.$probpartlist->[0].'.type') eq 'randomizetry') { + $partfortries = $probpartlist->[0]; + } else { + $partfortries = ''; + } + } + } + if ($partfortries ne '') { + $numtries = $Apache::lonhomework::history{"resource.$partfortries.tries"}; + $reqtries = &Apache::lonnet::EXT("resource.$partfortries.randomizeontries"); + } + } + if (($env{'request.state'} eq "construct") + || ($symb eq '') + || ($Apache::lonhomework::type eq 'practice') + || ($Apache::lonhomework::history{'resource.CODE'}) + || (($env{'form.code_for_randomlist'}) && ($target eq 'analyze'))) { &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['rndseed']); $rndseed=$env{'form.rndseed'}; @@ -512,13 +688,19 @@ sub setup_rndseed { if (!$rndseed) { $rndseed=time; } - $env{'form.rndseed'}=$rndseed; + unless ($env{'form.code_for_randomlist'}) { + $env{'form.rndseed'}=$rndseed; + } } - if (($env{'request.state'} eq "construct") && + if ((($env{'request.state'} eq "construct") || ($symb eq '')) && ($Apache::lonhomework::type eq 'randomizetry')) { - my $tries = $Apache::lonhomework::history{"resource.$Apache::inputtags::part.tries"}; - if ($tries) { - $rndseed += $tries; + if ($numtries) { + if (($reqtries =~ /^\d+$/) && ($reqtries > 1)) { + my $inc = int($numtries/$reqtries); + $rndseed += $inc; + } else { + $rndseed += $numtries; + } } $env{'form.'.$Apache::inputtags::part.'.rndseed'}=$rndseed; } @@ -540,7 +722,11 @@ sub setup_rndseed { $rndseed=join(':',&Apache::lonnet::digest($rndseed)); } } - if ($Apache::lonhomework::history{'resource.CODE'}) { + if (($env{'form.code_for_randomlist'}) && ($target eq 'analyze')) { + $env{'form.CODE'} = $env{'form.code_for_randomlist'}; + $rndseed=&Apache::lonnet::rndseed(); + undef($env{'form.CODE'}); + } elsif ($Apache::lonhomework::history{'resource.CODE'}) { $rndseed=&Apache::lonnet::rndseed(); } $set_safespace = 1; @@ -552,7 +738,7 @@ sub setup_rndseed { } unless (($target eq 'analyze') && (defined($rndseed))) { $rndseed=&Apache::lonnet::rndseed(); - my $curr_try = $Apache::lonhomework::history{"resource.$Apache::inputtags::part.tries"}; + my $curr_try = $numtries; if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') { $curr_try ++; } @@ -560,7 +746,6 @@ sub setup_rndseed { $rndseed = $1; } if ($curr_try) { - my $reqtries = &Apache::lonnet::EXT("resource.$Apache::inputtags::part.randomizeontries"); if (($reqtries =~ /^\d+$/) && ($reqtries > 1)) { my $inc = int(($curr_try-1)/$reqtries); $rndseed += $inc; @@ -570,6 +755,9 @@ sub setup_rndseed { } } $set_safespace = 1; + if ($target eq 'grade') { + $Apache::lonhomework::rawrndseed = $rndseed; + } } if ($set_safespace) { if ($safeeval) { @@ -599,53 +787,82 @@ sub problem_edit_action_button { sub problem_edit_buttons { my ($mode)=@_; +# Buttons that save + my $result = '
'; + if ($mode eq 'editxml') { + $result.=&problem_edit_action_button('subsaveedit','saveeditxml','s','Save and EditXML'); + $result.=&problem_edit_action_button('subsaveview','saveviewxml','v','Save and View'); + } else { + $result.=&problem_edit_action_button('subsaveedit','saveedit','s','Save and Edit'); + $result.=&problem_edit_action_button('subsaveview','saveview','v','Save and View'); + } + $result.="\n
\n"; # Buttons that do not save - my $result='
'. + $result .= '
'. &problem_edit_action_button('subdiscview','discard','d','Discard Edits and View',1); if ($mode eq 'editxml') { $result.=&problem_edit_action_button('subedit','edit','e','Edit',1); $result.=&problem_edit_action_button('subundo','undoxml','u','Undo',1); - $result.=&Apache::lonhtmlcommon::dragmath_button("LC_editxmltext",1); + if ($env{'environment.nocodemirror'}) { + $result.=&Apache::lonhtmlcommon::dragmath_button("LC_editxmltext",1); + } } else { $result.=&problem_edit_action_button('subeditxml','editxml','x','EditXML',1); $result.=&problem_edit_action_button('subundo','undo','u','Undo',1); } $result.="\n
"; -# Buttons that save - $result.='
'; - if ($mode eq 'editxml') { - $result.=&problem_edit_action_button('subsaveedit','saveeditxml','s','Save and EditXML'); - $result.=&problem_edit_action_button('subsaveview','saveviewxml','v','Save and View'); - } else { - $result.=&problem_edit_action_button('subsaveedit','saveedit','s','Save and Edit'); - $result.=&problem_edit_action_button('subsaveview','saveview','v','Save and View'); - } - $result.="\n
\n"; return $result; } sub problem_edit_header { - return ''. + my ($mode)=@_; + my $return = ''. &remember_problem_state('edit').' -
-
-'.&mt('Problem Editing').&Apache::loncommon::help_open_menu('Problem Editing','Problem_Editor_XML_Index',5,'Authoring').' -
'. -''. -&problem_edit_buttons().' -
-'.&Apache::lonxml::message_location().' -
-'. - '" + $result.=&Apache::edit::start_table($token)."" .'' - .'' + .'' .'' - .'
'; +
+
+ '.&mt('Problem Editing').$mode.&Apache::loncommon::help_open_menu('Problem Editing','Problem_Editor_XML_Index',5,'Authoring').' +
'. + ''. + &problem_edit_buttons(); + + $return .= '
' . &Apache::lonxml::message_location(); + $return .= ' + '; + + $return .= ' + +
'; + return $return; } + sub problem_edit_footer { + my $resource = $env{'request.ambiguous'}; return '

'. "\n\n".&Apache::loncommon::end_page(); @@ -728,7 +945,7 @@ $show_all @@ -830,7 +1047,7 @@ sub initialize_storage { } %Apache::lonhomework::history= &Apache::lonnet::tmprestore($namespace,'',$domain,$name); - my ($temp)=keys %Apache::lonhomework::history ; + my ($temp)=keys(%Apache::lonhomework::history); &Apache::lonxml::debug("Return message of $temp"); } else { %Apache::lonhomework::history= @@ -838,7 +1055,7 @@ sub initialize_storage { } #ignore error conditions - my ($temp)=keys %Apache::lonhomework::history ; + my ($temp)=keys(%Apache::lonhomework::history); if ($temp =~ m/^error:.*/) { %Apache::lonhomework::history=(); } } @@ -846,11 +1063,36 @@ 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 +1114,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 +1211,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 @@ -947,7 +1329,7 @@ sub store_aggregates { } } } - if (keys (%aggregate) > 0) { + if (keys(%aggregate) > 0) { &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate, $cdomain,$cname); } @@ -975,7 +1357,7 @@ sub checkout_msg {

$lt{'warning'}

- +
ENDCHECKOUT } @@ -1009,7 +1391,7 @@ sub firstaccess_msg { $result .= (< - + ENDCHECKOUT return $result; @@ -1044,11 +1426,13 @@ sub init_problem_globals { @Apache::inputtags::importlist = (); @Apache::inputtags::previous=(); @Apache::inputtags::previous_version=(); + $Apache::inputtags::leniency=''; $Apache::structuretags::printanswer='No'; @Apache::structuretags::whileconds=(); @Apache::structuretags::whilebody=(); @Apache::structuretags::whileline=(); $Apache::lonhomework::scantronmode=0; + $Apache::lonhomework::randomizetrypart=0; undef($Apache::lonhomework::name); undef($Apache::lonhomework::default_type); undef($Apache::lonhomework::type); @@ -1059,8 +1443,11 @@ sub reset_problem_globals { undef(%Apache::lonhomework::history); undef(%Apache::lonhomework::results); undef($Apache::inputtags::part); + undef($Apache::inputtags::leniency); if ($type eq 'Task') { undef($Apache::inputtags::slot_name); + } elsif ($type eq 'problem') { + undef($Apache::lonhomework::rawrndseed); } #don't undef this, lonhomework.pm takes care of this, we use this to #detect if we try to do 2 problems in one file @@ -1069,6 +1456,7 @@ sub reset_problem_globals { undef($Apache::lonhomework::default_type); undef($Apache::lonhomework::type); undef($Apache::lonhomework::scantronmode); + undef($Apache::inputtags::randomizetrypart); undef($Apache::lonhomework::ignore_response_errors); undef(@Apache::functionplotresponse::callscripts); &Apache::lonhomework::reset_show_problem_status(); @@ -1162,13 +1550,17 @@ sub start_problem { my $resource_due; my $name= &get_resource_name($parstack,$safeeval); - my ($result,$form_tag_start,$slot_name,$slot); + my ($result,$form_tag_start,$slot_name,$slot,$probpartlist,$firstaccres); if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' || $target eq 'tex') { if ($env{'form.markaccess'}) { my @interval=&Apache::lonnet::EXT("resource.0.interval"); - &Apache::lonnet::set_first_access($interval[1]); + my ($timelimit) = ($interval[0] =~ /^(\d+)/); + my $is_set = &Apache::lonnet::set_first_access($interval[1],$timelimit); + unless (($is_set eq 'ok') || ($is_set eq 'already_set')) { + $firstaccres = $is_set; + } } ($status,$accessmsg,$slot_name,$slot) = @@ -1178,9 +1570,20 @@ sub start_problem { if ($target eq 'web' || $target eq 'webgrade' || $target eq 'tex' || $target eq 'edit') { - ($result,$form_tag_start) = + ($result,$form_tag_start,$probpartlist) = &page_start($target,$token,$tagstack,$parstack,$parser,$safeeval, $name); + } elsif (($target eq 'grade') && ($Apache::lonhomework::type eq 'randomizetry')) { + my ($symb)= &Apache::lonnet::whichuser(); + if ($symb ne '') { + my $navmap = Apache::lonnavmaps::navmap->new(); + if (ref($navmap)) { + my $res = $navmap->getBySymb($symb); + if (ref($res)) { + $probpartlist = $res->parts(); + } + } + } } if ($target eq 'tex' and $env{'request.symb'} =~ m/\.page_/) {$result='';} @@ -1189,14 +1592,37 @@ sub start_problem { if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' || $target eq 'tex') { + my ($symb) = &Apache::lonnet::whichuser(); #handle rand seed in construction space - my $rndseed=&setup_rndseed($safeeval,$target); - my ($symb)=&Apache::lonnet::whichuser(); + if (($env{'request.state'} eq 'construct') || ($symb eq '')) { + my $partorder=&Apache::lonnet::metadata($env{'request.uri'},'partorder'); + if ($partorder ne '') { + @{$probpartlist} = split(/,/,$partorder); + } + } + my $rndseed=&setup_rndseed($safeeval,$target,$probpartlist); + if (($target eq 'grade') && &Apache::response::submitted()) { + if ($Apache::lonhomework::type eq 'randomizetry') { + $Apache::lonhomework::results{'resource.0.rndseed'}=$rndseed; + } else { + my @parts; + if (ref($probpartlist) eq 'ARRAY') { + @parts = @{$probpartlist}; + } + unless (@parts) { + $Apache::lonhomework::results{'resource.0.rndseed'}=$Apache::lonhomework::rawrndseed; + } + } + } if ($env{'request.state'} ne "construct" && ($symb eq '' || $Apache::lonhomework::type eq 'practice')) { + my $rndseedval = $rndseed; + if (($symb eq '') && ($Apache::lonhomework::type eq 'randomizetry')) { + $rndseedval = $env{'form.rndseed'}; + } $form_tag_start.=''. + $rndseedval.'" />'. ''; if (exists($env{'form.username'})) { @@ -1218,12 +1644,19 @@ sub start_problem { $form_tag_start.=&practice_problem_header(); } $form_tag_start.='
'; - } elsif (($env{'request.state'} ne "construct") && - ($Apache::lonhomework::type eq 'randomizetry') && - ($status eq 'CAN_ANSWER')) { - my $reqtries = &Apache::lonnet::EXT("resource.$Apache::inputtags::part.randomizeontries"); - my $problemstatus = &get_problem_status($Apache::inputtags::part); - $form_tag_start.=&randomizetry_problem_header($problemstatus,$reqtries); + } + if (($env{'request.state'} ne "construct") && + ($Apache::lonhomework::type eq 'randomizetry') && + ($status eq 'CAN_ANSWER')) { + my @parts; + if (ref($probpartlist) eq 'ARRAY') { + @parts = @{$probpartlist}; + } + unless (@parts) { + my $reqtries = &Apache::lonnet::EXT("resource.$Apache::inputtags::part.randomizeontries"); + my $problemstatus = &get_problem_status($Apache::inputtags::part); + $form_tag_start.=&randomizetry_problem_header($problemstatus,$reqtries,$symb); + } } my $expression='$external::datestatus="'.$status.'";'; @@ -1260,6 +1693,10 @@ sub start_problem { } elsif ($status eq 'UNCHECKEDOUT') { $msg.=&checkout_msg(); } elsif ($status eq 'NOT_YET_VIEWED') { + if ($firstaccres) { + $msg .= '

'. + &mt('A problem occurred when trying to start the timer.').'

'; + } $msg.=&firstaccess_msg($accessmsg,$symb); } elsif ($status eq 'NOT_IN_A_SLOT') { $msg.=&Apache::bridgetask::add_request_another_attempt_button("Sign up for time to work"); @@ -1335,6 +1772,10 @@ sub start_problem { $result.= ''. &practice_problem_header().'
'; + } elsif ($Apache::lonhomework::type eq 'randomizetry') { + my $reqtries = &Apache::lonnet::EXT("resource.$Apache::inputtags::part.randomizeontries"); + my $problemstatus = &get_problem_status($Apache::inputtags::part); + $result.=&randomizetry_problem_header($problemstatus,$reqtries); } } # if we are viewing someone else preserve that info @@ -1398,7 +1839,7 @@ sub end_problem { my $id = $Apache::inputtags::part; my $weight = &Apache::lonnet::EXT("resource.$id.weight"); my $packages=&Apache::lonnet::metadata($env{'request.uri'},'packages'); - my @packages = split /,/,$packages; + my @packages = split(/,/,$packages); my $allow_print_points = 0; foreach my $partial_key (@packages) { if ($partial_key=~m/^part_0$/) { @@ -1536,7 +1977,24 @@ sub end_problem { } } if ($target eq 'web') { - $result.=&Apache::functionplotresponse::init_script(); + $result.=&Apache::functionplotresponse::init_script(); + if ($Apache::lonhomework::default_type eq 'randomizetry') { + my ($symb) = &Apache::lonnet::whichuser(); + if ((($env{'request.state'} eq 'construct') || ($symb eq '')) && + ($status eq 'CAN_ANSWER')) { + unless (@Apache::inputtags::partlist > 1) { + $result.= <<"ENDJS"; + +ENDJS + } + } + } } if ($target eq 'grade') { &Apache::lonhomework::showhash(%Apache::lonhomework::results); @@ -1630,6 +2088,8 @@ sub end_library { && ($#$tagstack eq 0 && $$tagstack[0] eq 'library') && $env{'request.state'} eq "construct") { $result.=''.&Apache::loncommon::end_page({'discussion' => 1}); + } elsif ($target eq 'meta') { + $result.=&Apache::response::meta_response_order(); } if ( $#$tagstack eq 0 && $$tagstack[0] eq 'library') { &reset_problem_globals('library'); @@ -2057,9 +2517,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,''); } @@ -2106,7 +2572,12 @@ sub start_randomlist { } if (@randomlist) { my @idx_arr = (0 .. $#randomlist); - &Apache::structuretags::shuffle(\@idx_arr); + if ($env{'form.code_for_randomlist'}) { + &Apache::structuretags::shuffle(\@idx_arr,$target); + undef($env{'form.code_for_randomlist'}); + } else { + &Apache::structuretags::shuffle(\@idx_arr); + } my $bodytext = ''; my $show=$#randomlist; my $showarg=&Apache::lonxml::get_param('show',$parstack,$safeeval); @@ -2126,8 +2597,8 @@ sub start_randomlist { } } } - for(0 .. $show) { - $bodytext .= "$randomlist[ $idx_arr[$_] ]"; + for my $i (0 .. $show) { + $bodytext .= "$randomlist[ $idx_arr[$i] ]"; } &Apache::lonxml::newparser($parser,\$bodytext); } @@ -2145,10 +2616,10 @@ sub start_randomlist { } sub shuffle { - my $a=shift; + my ($a,$target) = @_; my $i; if (ref($a) eq 'ARRAY' && @$a) { - &Apache::response::pushrandomnumber(); + &Apache::response::pushrandomnumber(undef,$target); for($i=@$a;--$i;) { my $j=int(&Math::Random::random_uniform() * ($i+1)); next if $i == $j; @@ -2232,6 +2703,7 @@ sub start_part { my $id= &Apache::lonxml::get_id($parstack,$safeeval); $Apache::inputtags::part=$id; push(@Apache::inputtags::partlist,$id); + $Apache::inputtags::leniency=''; @Apache::inputtags::response=(); @Apache::inputtags::previous=(); @Apache::inputtags::previous_version=(); @@ -2241,6 +2713,14 @@ sub start_part { my $hidden=&Apache::loncommon::check_if_partid_hidden($Apache::inputtags::part); my $newtype=&Apache::lonnet::EXT("resource.$id.type"); if ($newtype) { $Apache::lonhomework::type=$newtype; } + if ($Apache::lonhomework::type eq 'randomizetry') { + my $rndseed=&setup_rndseed($safeeval,$target); + if (($target eq 'grade') && &Apache::response::submitted()) { + $Apache::lonhomework::results{"resource.$id.rndseed"}=$rndseed; + } + } elsif (($target eq 'grade') && &Apache::response::submitted()) { + $Apache::lonhomework::results{"resource.$id.rndseed"}=$Apache::lonhomework::rawrndseed; + } my $in_order_show=&ordered_show_check(); my $expression='$external::part=\''.$Apache::inputtags::part.'\';'; $expression.='$external::type=\''.$Apache::lonhomework::type.'\';'; @@ -2291,7 +2771,7 @@ sub start_part { } my $weight = &Apache::lonnet::EXT("resource.$id.weight"); my $allkeys=&Apache::lonnet::metadata($env{'request.uri'},'packages'); - my @allkeys = split /,/,$allkeys; + my @allkeys = split(/,/,$allkeys); my $allow_print_points = 0; foreach my $partial_key (@allkeys) { if ($partial_key=~m/^part_(.*)$/) { @@ -2313,20 +2793,14 @@ sub start_part { } elsif ($target eq 'web') { if ($status eq 'CAN_ANSWER') { my $problemstatus = &get_problem_status($Apache::inputtags::part); - my $probrandomize = &Apache::lonnet::EXT("resource.$Apache::inputtags::partlist[0].type"); - my $probrandtries = &Apache::lonnet::EXT("resource.$Apache::inputtags::partlist[0].randomizeontries"); my $num = scalar(@Apache::inputtags::partlist)-1; - if ($probrandomize eq 'randomizetry') { - if (&Apache::lonnet::EXT("resource.$Apache::inputtags::part.type") ne 'randomizetry') { - $result .= &randomizetry_part_header($problemstatus,'none',$num); - } else { - my $reqtries = &Apache::lonnet::EXT("resource.$Apache::inputtags::part.randomizeontries"); - if ($probrandtries ne $reqtries) { - $result .= &randomizetry_part_header($problemstatus,$reqtries,$num); - } - } - } elsif (&Apache::lonnet::EXT("resource.$Apache::inputtags::part.type") eq 'randomizetry') { - my $reqtries = &Apache::lonnet::EXT("resource.$Apache::inputtags::part.randomizeontries"); + if ((($Apache::lonhomework::default_type eq 'randomizetry') || + ($Apache::lonhomework::randomizetrypart)) && + ($Apache::lonhomework::type ne 'randomizetry')) { + $result .= &randomizetry_part_header($problemstatus,'none',$num); + } elsif ($Apache::lonhomework::type eq 'randomizetry') { + $Apache::lonhomework::randomizetrypart = 1; + my $reqtries = &Apache::lonnet::EXT("resource.$id.randomizeontries"); $result .= &randomizetry_part_header($problemstatus,$reqtries,$num); } } @@ -2389,6 +2863,7 @@ sub end_part { } pop @Apache::inputtags::status; $Apache::inputtags::part=''; + $Apache::inputtags::leniency=''; $Apache::lonhomework::type = $Apache::lonhomework::default_type; return $result; } @@ -2539,17 +3014,22 @@ sub end_startouttext { if ($target eq 'edit') { my $areaid = 'homework_edit_'.$Apache::lonxml::curdepth; $text=&Apache::lonxml::get_all_text("endouttext",$parser,$style); - $result.=&Apache::edit::start_table($token)."
".&mt('Text Block')."
".&Apache::loncommon::insert_folding_button() + ." ".&mt('Text Block')."'.&mt('Delete?').' ' .&Apache::edit::deletelist($target,$token) .'' - .&Apache::lonhtmlcommon::dragmath_button($areaid,1) - .''; + if ($env{'environment.nocodemirror'}) { + $result.=&Apache::lonhtmlcommon::dragmath_button($areaid,1); + } else { + $result.=' '; + } + $result.='' .&Apache::edit::insertlist($target,$token) .'' . + .'' . &Apache::loncommon::helpLatexCheatsheet(). &Apache::edit::end_row(). &Apache::edit::start_spanning_row()."\n". @@ -2640,7 +3120,7 @@ sub practice_problem_header { } sub randomizetry_problem_header { - my ($problemstatus,$reqtries) = @_; + my ($problemstatus,$reqtries,$symb) = @_; my ($header,$text); if ($reqtries > 1) { $header = &mt('New Problem Variation After Every [quant,_1,Try,Tries]',$reqtries); @@ -2660,8 +3140,13 @@ sub randomizetry_problem_header { $text = &mt('A new variation will be generated after each try until correct or tries limit is reached.'); } } - return '

'.$header.'

'. - ''.$text.'
'; + if (($env{'request.state'} eq "construct") || ($symb eq '')) { + return ''; + } else { + return '

'.$header.'

'. + ''.$text.'
'; + } } sub randomizetry_part_header { @@ -2669,7 +3154,7 @@ sub randomizetry_part_header { my ($header,$text); if ($reqtries eq 'none') { $header = &mt('No Question Variation'); - $text = &mt('For this question there will no new variation after a try.'); + $text = &mt('For this question there will be no new variation after a try.'); } elsif ($reqtries > 1) { $header = &mt('New Question Variation After Every [quant,_1,Try,Tries]',$reqtries); if (($problemstatus eq 'no') ||