--- loncom/homework/lonhomework.pm 2007/08/31 19:32:57 1.273 +++ loncom/homework/lonhomework.pm 2008/11/19 18:34:56 1.299 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Homework handler # -# $Id: lonhomework.pm,v 1.273 2007/08/31 19:32:57 albertel Exp $ +# $Id: lonhomework.pm,v 1.299 2008/11/19 18:34:56 jms Exp $ # # Copyright Michigan State University Board of Trustees # @@ -49,11 +49,11 @@ use Apache::matchresponse(); use Apache::chemresponse(); use Apache::drawimage(); use Apache::Constants qw(:common); -use HTML::Entities(); use Apache::loncommon(); use Apache::lonlocal; use Time::HiRes qw( gettimeofday tv_interval ); -use Apache::lonnet(); +use HTML::Entities(); +use File::Copy(); # FIXME - improve commenting @@ -63,14 +63,42 @@ BEGIN { } -# -# Decides what targets to render for. -# Implicit inputs: -# Various session environment variables: -# request.state - published - is a /res/ resource -# uploaded - is a /uploaded/ resource -# contruct - is a /priv/ resource -# form.grade_target - a form parameter requesting a specific target +=pod + +=item set_bubble_lines() + +Called at analysis time to set the bubble lines +hash for the problem.. This should be called in the +end_problemtype tag in analysis mode. + +We fetch the hash of part id counters from lonxml + and push them into analyze:{part_id.bubble_lines}. + +=cut + +sub set_bubble_lines { + my %bubble_counters = &Apache::lonxml::get_bubble_line_hash(); + + foreach my $key (keys(%bubble_counters)) { + $Apache::lonhomework::analyze{"$key.bubble_lines"} = + $bubble_counters{"$key"}; + } +} + +=pod + +=item get_target() + +Decides what targets to render for. +Implicit inputs: + Various session environment variables: + request.state - published - is a /res/ resource + uploaded - is a /uploaded/ resource + contruct - is a /priv/ resource + form.grade_target - a form parameter requesting a specific target + +=cut + sub get_target { &Apache::lonxml::debug("request.state = $env{'request.state'}"); if( defined($env{'form.grade_target'})) { @@ -115,8 +143,8 @@ sub get_target { if ($env{'form.problemstate'} eq 'WEB_GRADE') { #$env{'form.webgrade'} = 'yes'; return ('grade','webgrade','answer'); - } elsif ( $env{'form.problemmode'} eq &mt('View') || - $env{'form.problemmode'} eq &mt('Discard Edits and View')) { + } elsif (($env{'form.problemmode'} eq 'view') || + ($env{'form.problemmode'} eq 'discard')) { if ( defined($env{'form.submitted'}) && (!defined($env{'form.resetdata'})) && (!defined($env{'form.newrandomization'}))) { @@ -124,10 +152,9 @@ sub get_target { } else { return ('web','answer'); } - } elsif ( $env{'form.problemmode'} eq &mt('Edit') || - $env{'form.problemmode'} eq 'Edit') { + } elsif ($env{'form.problemmode'} eq 'edit') { if ( $env{'form.submitted'} eq 'edit' ) { - if ( $env{'form.submit'} eq &mt('Submit Changes and View') ) { + if ( $env{'form.submitbutton'} eq &mt('Save and View') ) { return ('modified','web','answer'); } else { return ('modified','no_output_web','edit'); @@ -188,58 +215,6 @@ sub proctor_checked_in { return 0; } -sub check_ip_acc { - my ($acc)=@_; - &Apache::lonxml::debug("acc is $acc"); - if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) { - return 1; - } - my $allowed=0; - my $ip=$env{'request.host'} || $ENV{'REMOTE_ADDR'}; - - my $name; - foreach my $pattern (split(',',$acc)) { - $pattern =~ s/^\s*//; - $pattern =~ s/\s*$//; - if ($pattern =~ /\*$/) { - #35.8.* - $pattern=~s/\*//; - if ($ip =~ /^\Q$pattern\E/) { $allowed=1; } - } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) { - #35.8.3.[34-56] - my $low=$2; - my $high=$3; - $pattern=$1; - if ($ip =~ /^\Q$pattern\E/) { - my $last=(split(/\./,$ip))[3]; - if ($last <=$high && $last >=$low) { $allowed=1; } - } - } elsif ($pattern =~ /^\*/) { - #*.msu.edu - $pattern=~s/\*//; - if (!defined($name)) { - use Socket; - my $netaddr=inet_aton($ip); - ($name)=gethostbyaddr($netaddr,AF_INET); - } - if ($name =~ /\Q$pattern\E$/i) { $allowed=1; } - } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) { - #127.0.0.1 - if ($ip =~ /^\Q$pattern\E/) { $allowed=1; } - } else { - #some.name.com - if (!defined($name)) { - use Socket; - my $netaddr=inet_aton($ip); - ($name)=gethostbyaddr($netaddr,AF_INET); - } - if ($name =~ /\Q$pattern\E$/i) { $allowed=1; } - } - if ($allowed) { last; } - } - return $allowed; -} - sub check_slot_access { my ($id,$type)=@_; @@ -270,9 +245,9 @@ sub check_slot_access { } } - my @slots= - (split(':',&Apache::lonnet::EXT("resource.0.availablestudent")), - split(':',&Apache::lonnet::EXT("resource.0.available"))); + my $availablestudent = &Apache::lonnet::EXT("resource.0.availablestudent"); + my $available = &Apache::lonnet::EXT("resource.0.available"); + my @slots= (split(':',$availablestudent),split(':',$available)); # if (!@slots) { # return ($status,$datemsg); @@ -286,7 +261,7 @@ sub check_slot_access { &Apache::lonhomework::showhash(%slot); if ($slot{'starttime'} < time && $slot{'endtime'} > time && - &check_ip_acc($slot{'ip'})) { + &Apache::loncommon::check_ip_acc($slot{'ip'})) { &Apache::lonxml::debug("$slot is good"); $slotstatus='NEEDS_CHECKIN'; $returned_slot=\%slot; @@ -362,8 +337,16 @@ sub check_slot_access { return ($slotstatus,$datemsg,$slot_name,$returned_slot); } -# JB, 9/24/2002: Any changes in this function may require a change -# in lonnavmaps::resource::getDateStatus. + +=pod + +=item check_access() + +JB, 9/24/2002: Any changes in this function may require a change +in lonnavmaps::resource::getDateStatus. + +=cut + sub check_access { my ($id) = @_; my $date =''; @@ -376,8 +359,8 @@ sub check_access { if ($env{'request.state'} eq "construct") { if ($env{'form.problemstate'}) { if ($env{'form.problemstate'} =~ /^CANNOT_ANSWER/) { - if ( ! ($env{'form.problemstate'} eq 'CANNOT_ANSWER_correct' && - lc($Apache::lonhomework::problemstatus) eq 'no')) { + if ( ! ($env{'form.problemstate'} eq 'CANNOT_ANSWER_correct' + && &hide_problem_status())) { return ('CANNOT_ANSWER', &mt('is in this state due to author settings.')); } @@ -399,7 +382,8 @@ sub check_access { &Apache::lonxml::debug("symb:".$symb); #if ($env{'request.state'} ne "construct" && $symb ne '') { if ($env{'request.state'} ne "construct") { - my $allowed=&check_ip_acc(&Apache::lonnet::EXT("resource.$id.acc")); + my $idacc = &Apache::lonnet::EXT("resource.$id.acc"); + my $allowed=&Apache::loncommon::check_ip_acc($idacc); if (!$allowed && ($Apache::lonhomework::browse ne 'F')) { $status='INVALID_ACCESS'; $date=&mt("can not be accessed from your location."); @@ -436,7 +420,7 @@ sub check_access { $date = &mt("an indeterminate date"); $passed = 0; } else { if (time < $date) { $passed = 0; } else { $passed = 1; } - $date = localtime $date; + $date = &Apache::lonlocal::locallocaltime($date); } if (!$passed) { $type=$temp; last; } } @@ -467,24 +451,28 @@ sub check_access { # if (correct and show prob status) or excused then CANNOT_ANSWER if(($Apache::lonhomework::history{"resource.$id.solved"}=~/^correct/ && - lc($Apache::lonhomework::problemstatus) ne 'no') + &show_problem_status()) || $Apache::lonhomework::history{"resource.$id.solved"}=~/^excused/) { $status = 'CANNOT_ANSWER'; } + if ($status eq 'CANNOT_ANSWER' + && &show_answer_problem_status()) { + $status = 'SHOW_ANSWER'; + } } if ($status eq 'CAN_ANSWER' || $status eq 'CANNOT_ANSWER') { - my $interval=&Apache::lonnet::EXT("resource.$id.interval"); - &Apache::lonxml::debug("looking for interval $interval"); - if ($interval) { - my $first_access=&Apache::lonnet::get_first_access('map'); + my @interval=&Apache::lonnet::EXT("resource.$id.interval"); + &Apache::lonxml::debug("looking for interval @interval"); + if ($interval[0]) { + my $first_access=&Apache::lonnet::get_first_access($interval[1]); &Apache::lonxml::debug("looking for accesstime $first_access"); if (!$first_access) { $status='NOT_YET_VIEWED'; my $due_date = &due_date($id); my $seconds_left = $due_date - time; - if ($seconds_left > $interval || $due_date eq '') { - $seconds_left = $interval; + if ($seconds_left > $interval[0] || $due_date eq '') { + $seconds_left = $interval[0]; } $datemsg=&seconds_to_human_length($seconds_left); } @@ -506,22 +494,32 @@ sub check_access { return ($status,$datemsg); } -# this should work exactly like the copy in lonnavmaps.pm + + +=pod + +=item due_date() + + this should work exactly like the copy in lonnavmaps.pm + +=cut + sub due_date { my ($part_id,$symb,$udom,$uname)=@_; my $date; - my $interval= &Apache::lonnet::EXT("resource.$part_id.interval",$symb, + my @interval= &Apache::lonnet::EXT("resource.$part_id.interval",$symb, $udom,$uname); - &Apache::lonxml::debug("looking for interval $part_id $symb $interval"); + &Apache::lonxml::debug("looking for interval $part_id $symb @interval"); my $due_date= &Apache::lonnet::EXT("resource.$part_id.duedate",$symb, $udom,$uname); &Apache::lonxml::debug("looking for due_date $part_id $symb $due_date"); - if ($interval =~ /\d+/) { - my $first_access=&Apache::lonnet::get_first_access('map',$symb); - &Apache::lonxml::debug("looking for first_access $first_access"); + if ($interval[0] =~ /\d+/) { + my $first_access=&Apache::lonnet::get_first_access($interval[1],$symb); + &Apache::lonxml::debug("looking for first_access $first_access ($interval[1])"); if (defined($first_access)) { - $interval = $first_access+$interval; - $date = ($interval < $due_date)? $interval : $due_date; + my $interval = $first_access+$interval[0]; + $date = (!$due_date || $interval < $due_date) ? $interval + : $due_date; } else { $date = $due_date; } @@ -661,9 +659,9 @@ sub handle_save_or_undo { my $error=0; if ($env{'form.Undo'} eq &mt('undo')) { my $error=0; - if (!copy($file,$filetmp)) { $error=1; } - if ((!$error) && (!copy($filebak,$file))) { $error=1; } - if ((!$error) && (!move($filetmp,$filebak))) { $error=1; } + if (!&File::Copy::copy($file,$filetmp)) { $error=1; } + if ((!$error) && (!&File::Copy::copy($filebak,$file))) { $error=1; } + if ((!$error) && (!&File::Copy::move($filetmp,$filebak))) { $error=1; } if (!$error) { &Apache::lonxml::info("

". &mt("Undid changes, Switched [_1] and [_2]", @@ -685,10 +683,6 @@ sub handle_save_or_undo { my $fs=Apache::File->new(">$filebak"); if (defined($fs)) { print $fs $$problem; - &Apache::lonxml::info("".&mt("Making Backup to [_1]", - ''. - $filebak.''). - ""); } else { &Apache::lonxml::info("". &mt("Unable to make backup [_1]", @@ -699,9 +693,6 @@ sub handle_save_or_undo { my $fh=Apache::File->new(">$file"); if (defined($fh)) { print $fh $$result; - &Apache::lonxml::info("".&mt("Saving Modifications to [_1]", - ''. - $file.'' ).""); } else { &Apache::lonxml::info(''. &mt("Unable to write to [_1]", @@ -716,19 +707,27 @@ sub handle_save_or_undo { sub analyze_header { my ($request) = @_; + my $js = &Apache::structuretags::setmode_javascript(); my $result = - &Apache::loncommon::start_page('Analyzing a problem',undef); + &Apache::loncommon::start_page('Analyzing a problem',$js); $result .= &Apache::lonxml::message_location().'

'. + ''. &Apache::structuretags::remember_problem_state().' - - -
- -
+
+ + +
+ +
+
'; &Apache::lonxml::add_messages(\$result); $request->print($result); @@ -788,12 +787,20 @@ sub analyze { $i++; } } + if (!keys(%{ $analyze{$part.'.answer'} })) { + my $answer_part = + [''.&mt('Error').'']; + $seedexample{join("\0",$part,0,@{$answer_part})}= + $thisseed; + push( @{ $overall{$part.'.answer'}[0] }, + $answer_part); + } } } } &Apache::lonhtmlcommon::Update_PrgWin($request,\%prog_state, &mt('Analyzing Results')); - $request->print('
'.&mt('List of possible answers').': '); + $request->print('
'.&mt('List of possible answers').': '); foreach my $part (sort(keys(%allparts))) { if (defined(@{ $overall{$part.'.answer'} })) { for (my $i=0;$i 1, @@ -873,29 +919,40 @@ sub editxmlmode { 'onresize' => q[resize_textarea('LC_editxmltext','LC_aftertextarea')], 'onload' => q[resize_textarea('LC_editxmltext','LC_aftertextarea')], }}); - $result.=$start_page. &renderpage($request,$file,['no_output_web'],1). - &Apache::lonxml::message_location().' -
'. &Apache::structuretags::remember_problem_state().' - - - - -
- - -
- ' . $xml_help . ' +
+ +
+ '.&mt('Problem Editing').&Apache::loncommon::help_open_topic('Problem_Editor_XML_Index').' + + '.&Apache::loncommon::helpLatexCheatsheet().' +
+
+ + + + + + '.$dragmath_button.' +
+
+ + +
+
+ '.&Apache::lonxml::message_location().' +
+ ' . '
- -
'.&Apache::loncommon::end_page(); &Apache::lonxml::add_messages(\$result); @@ -904,9 +961,14 @@ sub editxmlmode { return ''; } -# -# Render the page in whatever target desired. -# +=pod + +=item renderpage() + + Render the page in whatever target desired. + +=cut + sub renderpage { my ($request,$file,$targets,$return_string) = @_; @@ -987,52 +1049,85 @@ sub finished_parsing { undef($Apache::lonhomework::parsing_a_task); } -# with no arg it returns a HTML

".&mt("To create a new $extension, select a template from the". - "pull-down menu below. Then click on the \"Create $extension\" button.")." + " list below. Then click on the \"Create $extension\" button.")."

"); if (defined($templatelist)) { - $request->print(""); + $request->print($templatelist); } - $request->print("
"); + $request->print('
'); $request->print("

".&Apache::loncommon::end_page()); } - return ''; + return; } sub update_construct_style { if ($env{'request.state'} eq "construct" - && $env{'form.problemmode'} eq &mt('View') + && $env{'form.problemmode'} eq 'view' && defined($env{'form.submitted'}) && !defined($env{'form.resetdata'}) && !defined($env{'form.newrandomization'})) { @@ -1078,8 +1171,8 @@ sub update_construct_style { &Apache::lonnet::delenv('construct\\.style'); } elsif ($env{'form.style_file'} && $env{'construct.style'} ne $env{'form.style_file'}) { - &Apache::lonnet::appenv('construct.style' => - $env{'form.style_file'}); + &Apache::lonnet::appenv({'construct.style' => + $env{'form.style_file'}}); } } } @@ -1092,13 +1185,6 @@ sub handler { $Apache::lonxml::debug=$env{'user.debug'}; $env{'request.uri'}=$request->uri; &setuppermissions(); - # some times multiple problemmodes are submitted, need to select - # the last one - if ( defined($env{'form.problemmode'}) && ref($env{'form.problemmode'}) ) { - my $mode=$env{'form.problemmode'}->[-1]; - undef $env{'form.problemmode'}; - $env{'form.problemmode'}=$mode; - } my $file=&Apache::lonnet::filelocation("",$request->uri); @@ -1127,12 +1213,11 @@ sub handler { ['problemmode']); if (!(defined $env{'form.problemmode'})) { #first visit to problem in construction space - $env{'form.problemmode'}='View'; + $env{'form.problemmode'}= 'view'; &renderpage($request,$file); - } elsif ($env{'form.problemmode'} eq &mt('EditXML') || - $env{'form.problemmode'} eq 'EditXML') { + } elsif ($env{'form.problemmode'} eq 'editxml') { &editxmlmode($request,$file); - } elsif ($env{'form.problemmode'} eq &mt('Calculate answers')) { + } elsif ($env{'form.problemmode'} eq 'calcanswers') { &analyze($request,$file); } else { &update_construct_style();