--- loncom/homework/structuretags.pm 2020/09/28 00:46:06 1.512.2.24 +++ loncom/homework/structuretags.pm 2024/01/13 17:48:57 1.512.2.24.2.9 @@ -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.24 2020/09/28 00:46:06 raeburn Exp $ +# $Id: structuretags.pm,v 1.512.2.24.2.9 2024/01/13 17:48:57 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -62,6 +62,7 @@ use Apache::lonxml; use Apache::londefdef; use Apache::lonenc(); use Apache::loncommon(); +use Apache::lonnavmaps; use Time::HiRes qw( gettimeofday tv_interval ); use HTML::Entities(); use lib '/home/httpd/lib/perl/'; @@ -272,7 +273,7 @@ sub homework_js { } return &Apache::loncommon::resize_textarea_js(). &Apache::loncommon::colorfuleditor_js(). - &setmode_javascript(). + &Apache::lonxml::setmode_javascript(). <<"JS"; -ENDSCRIPT -} - sub page_start { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$name, $extra_head)=@_; @@ -599,7 +585,9 @@ sub page_start { my ($symb,$courseid,$udom,$uname)=&Apache::lonnet::whichuser(); my ($path,$multiresp) = &Apache::loncommon::get_turnedin_filepath($symb,$uname,$udom); - if (($is_task) || ($needs_upload)) { + if ($env{'request.user_in_effect'}) { + $form_tag_start .= ' onsubmit="preventDefault();"'; + } elsif (($is_task) || ($needs_upload)) { $form_tag_start .= ' onsubmit="return file_submission_check(this,'."'$path','$multiresp'".');"'; } $form_tag_start.='>'."\n"; @@ -676,10 +664,10 @@ sub setup_rndseed { $reqtries = &Apache::lonnet::EXT("resource.$partfortries.randomizeontries"); } } - if (($env{'request.state'} eq "construct") + if (($env{'request.state'} eq "construct") || ($symb eq '') || ($Apache::lonhomework::type eq 'practice') - || ($Apache::lonhomework::history{'resource.CODE'}) + || ($Apache::lonhomework::history{'resource.CODE'}) || (($env{'form.code_for_randomlist'}) && ($target eq 'analyze'))) { &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['rndseed']); @@ -1092,9 +1080,9 @@ sub initialize_storage { 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, + Will call &store_aggregates() to increment totals for attempts, students, and corrects, if running user has student role. - + =cut @@ -1106,6 +1094,8 @@ sub finalize_storage { delete(@Apache::lonhomework::results{@remove}); my ($symb,$courseid,$domain,$name) = &Apache::lonnet::whichuser($given_symb); + my ($passback,$pbscope,$pbmap,$pbsymb,$pbtype,$crsdef,$ltinum, + $ltiref,$total,$possible,$dopassback); if ($env{'request.state'} eq 'construct' || $symb eq '' || $Apache::lonhomework::type eq 'practice') { @@ -1115,17 +1105,28 @@ sub finalize_storage { $namespace,'',$domain,$name); &Apache::lonxml::debug('Construct Store return message:'.$result); } else { - my ($laststore,$checkedparts,@parts,%postcorrect); + my ($laststore,$checkedparts,@parts,%postcorrect,%record); 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 (($env{'request.lti.login'}) || ($env{'request.deeplink.login'})) { + my ($map)=&Apache::lonnet::decode_symb($symb); + $map = &Apache::lonnet::clutter($map); + if ($env{'request.lti.login'}) { + ($passback,$pbscope,$pbmap,$pbsymb,$ltinum,$ltiref) = + &needs_lti_passback($courseid,$symb,$map); + } elsif ($env{'request.deeplink.login'}) { + ($passback,$pbscope,$pbmap,$pbsymb,$crsdef,$ltinum,$ltiref) = + &needs_linkprot_passback($courseid,$symb,$map); + } + } 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); + %record = &Apache::lonnet::restore($symb,$courseid,$domain,$name); if ($record{'version'}) { my ($newversion,$oldversion,$oldtimestamp); if ($Apache::lonhomework::history{'version'}) { @@ -1201,8 +1202,98 @@ sub finalize_storage { } } } + if ($passback) { + foreach my $key (keys(%Apache::lonhomework::results)) { + if ($key =~ /^resource\.([^\.]+)\.solved$/) { + my $part = $1; + if ((($Apache::lonhomework::results{$key} =~ /^correct_/) || + ($Apache::lonhomework::results{$key} eq 'incorrect_attempted')) && + ($Apache::lonhomework::results{"resource.$part.tries"})) { + $dopassback = 1; + last; + } + } + } + } + if (($dopassback) && ($pbscope eq 'resource') && ($pbsymb eq $symb)) { + $total = 0; + $possible = 0; + my $navmap = Apache::lonnavmaps::navmap->new(); + if (ref($navmap)) { + my $res = $navmap->getBySymb($symb); + if (ref($res)) { + my $partlist = $res->parts(); + if (ref($partlist) eq 'ARRAY') { + foreach my $part (@{$partlist}) { + unless (exists($Apache::lonhomework::results{"resource.$part.solved"})) { + next if ($Apache::lonhomework::record{"resource.$part.solved"} =~/^excused/); + my $weight = &Apache::lonnet::EXT("resource.$part.weight",$symb); + $possible += $weight; + if (($record{'version'}) && (exists($record{"resource.$part.awarded"}))) { + my $awarded = $record{"resource.$part.awarded"}; + if ($awarded) { + $total += $weight * $awarded; + } + } + } + } + } + } + } + foreach my $key (keys(%Apache::lonhomework::results)) { + if ($key =~ /^resource\.([^\.]+)\.awarded$/) { + my $part = $1; + my $weight = &Apache::lonnet::EXT("resource.$part.weight",$symb); + $possible += $weight; + my $awarded = $Apache::lonhomework::results{$key}; + if ($awarded) { + $total += $weight * $awarded; + } + } + } + } &Apache::lonxml::debug('Store return message:'.$result); &store_aggregates($symb,$courseid); + if ($dopassback) { + my $scoreformat = 'decimal'; + if (($env{'request.lti.login'}) || ($env{'request.deeplink.login'})) { + if (ref($ltiref) eq 'HASH') { + if ($ltiref->{'scoreformat'} =~ /^(decimal|ratio|percentage)$/) { + $scoreformat = $1; + } + } + } + my ($pbid,$pburl,$pbtype); + if ($env{'request.lti.login'}) { + $pbid = $env{'request.lti.passbackid'}; + $pburl = $env{'request.lti.passbackurl'}; + $pbtype = 'lti'; + } elsif ($env{'request.deeplink.login'}) { + $pbid = $env{'request.linkprotpbid'}; + $pburl = $env{'request.linkprotpburl'}; + $pbtype = 'linkprot'; + } + my $ltigrade = { + 'ltinum' => $ltinum, + 'lti' => $ltiref, + 'crsdef' => $crsdef, + 'cid' => $courseid, + 'uname' => $env{'user.name'}, + 'udom' => $env{'user.domain'}, + 'pbid' => $pbid, + 'pburl' => $pburl, + 'pbtype' => $pbtype, + 'scope' => $pbscope, + 'pbmap' => $pbmap, + 'pbsymb' => $pbsymb, + 'format' => $scoreformat, + }; + if ($pbscope eq 'resource') { + $ltigrade->{'total'} = $total; + $ltigrade->{'possible'} = $possible; + } + push(@Apache::lonhomework::ltipassback,$ltigrade); + } } } else { &Apache::lonxml::debug('Nothing to store'); @@ -1210,6 +1301,104 @@ sub finalize_storage { return $result; } +sub needs_lti_passback { + my ($courseid,$symb,$map) = @_; + if (($env{'request.lti.passbackid'}) && ($env{'request.lti.passbackurl'})) { + if ($courseid =~ /^($LONCAPA::match_domain)_($LONCAPA::match_courseid)$/) { + my ($cdom,$cnum) = ($1,$2); + my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider'); + if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') { + if ($lti{$env{'request.lti.login'}}{'passback'}) { + my $itemnum = $env{'request.lti.login'}; + my ($ltiscope,$ltiuri,$ltisymb) = + &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'}, + $cdom,$cnum,1); + my ($passback,$ltimap); + if ($ltiscope eq 'resource') { + if ($ltisymb eq $symb) { + $passback = 1; + } + } elsif ($ltiscope eq 'map') { + if ($ltiuri eq $map) { + $passback = 1; + $ltimap = $map; + } + } elsif ($ltiscope eq 'course') { + if (($env{'request.lti.uri'} eq "/$cdom/$cnum") || ($env{'request.lti.uri'} eq '')) { + $passback = 1; + } + } + return ($passback,$ltiscope,$ltimap,$ltisymb,$itemnum,$lti{$itemnum}); + } + } + } + } + return; +} + +sub needs_linkprot_passback { + my ($courseid,$symb,$map) = @_; + if (($env{'request.linkprotpbid'}) && ($env{'request.linkprotpburl'})) { + if ($courseid =~ /^($LONCAPA::match_domain)_($LONCAPA::match_courseid)$/) { + my ($cdom,$cnum) = ($1,$2); + my ($deeplink_symb,$deeplink_map,$deeplink,$passback); + $deeplink_symb = &Apache::loncommon::deeplink_login_symb($cnum,$cdom); + if ($deeplink_symb) { + if ($deeplink_symb =~ /\.(page|sequence)$/) { + $deeplink_map = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($deeplink_symb))[2]); + my $navmap = Apache::lonnavmaps::navmap->new(); + if (ref($navmap)) { + $deeplink = $navmap->get_mapparam(undef,$deeplink_map,'0.deeplink'); + } + } else { + $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$deeplink_symb); + $deeplink_map = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($deeplink_symb))[0]); + } + if (($deeplink ne '') && ($env{'request.linkprot'} ne '')) { + my ($itemid,$tinyurl) = split(/:/,$env{'request.linkprot'}); + if ($itemid =~ /^(\d+)(c|d)$/) { + my ($itemnum,$itemtype) = ($1,$2); + my ($crsdef,$lti_in_use); + if ($itemtype eq 'c') { + $crsdef = 1; + my %crslti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider'); + $lti_in_use = $crslti{$itemnum}; + } else { + my %domlti = &Apache::lonnet::get_domain_lti($cdom,'linkprot'); + $lti_in_use = $domlti{$itemnum}; + } + my ($state,$others,$listed,$scope,$protect,$display,$target,$exit) = split(/,/,$deeplink); + my ($passback,$pbscope); + if ($scope eq 'res') { + if ($deeplink_symb eq $symb) { + $passback = 1; + $pbscope = 'resource'; + } + } elsif ($scope eq 'map') { + if (&Apache::lonnet::clutter($deeplink_map) eq $map) { + $passback = 1; + $pbscope = 'nonrec'; + } + } elsif ($scope eq 'rec') { + if (&Apache::lonnet::clutter($deeplink_map) eq $map) { + $passback = 1; + $pbscope = 'map'; + } else { + my @recurseup = &Apache::lonnet::get_map_hierarchy($map,$env{'request.course.id'}); + if (grep(/^\Q$deeplink_map\E$/,@recurseup)) { + $passback = 1; + $pbscope = 'map'; + } + } + } + return ($passback,$pbscope,$deeplink_map,$deeplink_symb,$crsdef,$itemnum,$lti_in_use); + } + } + } + } + } +} + =pod =item check_correctness_changes() @@ -1559,15 +1748,12 @@ sub start_problem { $target eq 'tex') { if ($env{'form.markaccess'}) { my @interval=&Apache::lonnet::EXT("resource.0.interval"); - my $is_set = &Apache::lonnet::set_first_access($interval[1],$interval[0]); + 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) = - &Apache::lonhomework::check_slot_access('0','problem'); - push (@Apache::inputtags::status,$status); } if ($target eq 'web' || $target eq 'webgrade' || $target eq 'tex' @@ -1575,7 +1761,8 @@ sub start_problem { ($result,$form_tag_start,$probpartlist) = &page_start($target,$token,$tagstack,$parstack,$parser,$safeeval, $name); - } elsif (($target eq 'grade') && ($Apache::lonhomework::type eq 'randomizetry')) { + } elsif ((($target eq 'grade') && ($Apache::lonhomework::type eq 'randomizetry')) || + ($target eq 'answer')) { my ($symb)= &Apache::lonnet::whichuser(); if ($symb ne '') { my $navmap = Apache::lonnavmaps::navmap->new(); @@ -1588,6 +1775,20 @@ sub start_problem { } } + if (($target eq 'web') && ($env{'request.user_in_effect'})) { + &Apache::lonxml::get_all_text("/problem",$parser,$style); + return $result; + } + + if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' || + $target eq 'tex') { + + my ($symb)= &Apache::lonnet::whichuser(); + ($status,$accessmsg,$slot_name,$slot) = + &Apache::lonhomework::check_slot_access('0','problem',$symb,$probpartlist); + push (@Apache::inputtags::status,$status); + } + if ($target eq 'tex' and $env{'request.symb'} =~ m/\.page_/) {$result='';} if ($target eq 'analyze') { my $rndseed=&setup_rndseed($safeeval,$target); } @@ -1920,6 +2121,12 @@ sub end_problem { } } $result =~ s/INSERTTEXFRONTMATTERHERE/$frontmatter/; + } elsif ($target eq 'web') { + if ($env{'request.user_in_effect'}) { + &reset_problem_globals('problem'); + $result .= &Apache::lonhtmlcommon::set_compute_end_time(); + return $result; + } } my $status=$Apache::inputtags::status['-1']; @@ -2034,7 +2241,8 @@ ENDJS # computation: # if ($target eq 'web') { - $result .= &Apache::lonhtmlcommon::set_compute_end_time(); + $result .= &Apache::lonhtmlcommon::dash_to_minus_js(). + &Apache::lonhtmlcommon::set_compute_end_time(); # # Closing tags delayed so any tags # not in head can appear inside body, for valid xhtml. @@ -2857,7 +3065,11 @@ sub end_part { $gradestatus=''; } $result.=$gradestatus; - if ($$tagstack[-2] eq 'td' and $target eq 'tex') {$result.='\end{minipage}';} + if ($$tagstack[-2] eq 'td' and $target eq 'tex') { + if (not $env{'form.problem_split'}=~/yes/) { + $result.='\end{minipage}'; + } + } } elsif ($target eq 'edit') { $result.=&Apache::edit::end_table(); } elsif ($target eq 'modified') {