--- loncom/homework/lonhomework.pm 2007/04/06 21:21:34 1.266 +++ loncom/homework/lonhomework.pm 2011/10/31 19:35:03 1.329 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Homework handler # -# $Id: lonhomework.pm,v 1.266 2007/04/06 21:21:34 albertel Exp $ +# $Id: lonhomework.pm,v 1.329 2011/10/31 19:35:03 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -47,13 +47,14 @@ use Apache::externalresponse(); use Apache::rankresponse(); use Apache::matchresponse(); use Apache::chemresponse(); +use Apache::functionplotresponse(); 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,6 +64,28 @@ BEGIN { } +=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"}; + } +} + # # Decides what targets to render for. # Implicit inputs: @@ -89,7 +112,11 @@ sub get_target { } elsif ( $env{'form.grade_target'} eq 'webgrade' && ($Apache::lonhomework::queuegrade eq 'F' )) { return ($env{'form.grade_target'}); - } + } elsif ($env{'form.grade_target'} eq 'answer') { + if ($env{'form.answer_output_mode'} eq 'tex') { + return ($env{'form.grade_target'}); + } + } if ($env{'form.webgrade'} && ($Apache::lonhomework::modifygrades eq 'F' || $Apache::lonhomework::queuegrade eq 'F' )) { @@ -102,41 +129,46 @@ sub get_target { return ('web'); } } elsif ($env{'request.state'} eq "construct") { +# +# We are in construction space, editing and testing problems +# if ( defined($env{'form.grade_target'}) ) { return ($env{'form.grade_target'}); } if ( defined($env{'form.preview'})) { if ( defined($env{'form.submitted'})) { +# +# We are doing a problem preview +# return ('grade', 'web'); } else { return ('web'); } } else { - if ( $env{'form.problemmode'} eq &mt('View') || - $env{'form.problemmode'} eq &mt('Discard Edits and View')) { - if ( defined($env{'form.submitted'}) && - (!defined($env{'form.resetdata'})) && - (!defined($env{'form.newrandomization'}))) { - return ('grade', 'web','answer'); - } else { - return ('web','answer'); - } - } elsif ( $env{'form.problemmode'} eq &mt('Edit') || - $env{'form.problemmode'} eq 'Edit') { - if ( $env{'form.submitted'} eq 'edit' ) { - if ( $env{'form.submit'} eq &mt('Submit Changes and View') ) { - return ('modified','web','answer'); - } else { - return ('modified','no_output_web','edit'); - } - } else { - return ('no_output_web','edit'); - } + if ($env{'form.problemstate'} eq 'WEB_GRADE') { + return ('grade','webgrade','answer'); + } elsif ($env{'form.problemmode'} eq 'view') { + return ('grade','web','answer'); + } elsif ($env{'form.problemmode'} eq 'saveview') { + return ('modified','web','answer'); + } elsif ($env{'form.problemmode'} eq 'discard') { + return ('web','answer'); + } elsif (($env{'form.problemmode'} eq 'saveedit') || + ($env{'form.problemmode'} eq 'undo')) { + return ('modified','no_output_web','edit'); + } elsif ($env{'form.problemmode'} eq 'edit') { + return ('no_output_web','edit'); } else { return ('web'); } - } + } +# +# End of Construction Space +# } +# +# Huh? We are nowhere, so do nothing. +# return (); } @@ -146,16 +178,6 @@ sub setup_vars { # return ';$external::target='.$target.';'; } -sub createmenu { - my ($which,$request)=@_; - if ($which eq 'grade') { - $request->print(''); - } -} - sub proctor_checked_in { my ($slot_name,$slot,$type)=@_; my @possible_proctors=split(",",$slot->{'proctor'}); @@ -185,58 +207,6 @@ sub proctor_checked_in { return 0; } -$Apache::lonxml::browse=''; -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{'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)=@_; @@ -267,9 +237,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); @@ -283,7 +253,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; @@ -293,8 +263,8 @@ sub check_slot_access { } if ($slotstatus eq 'NEEDS_CHECKIN' && &proctor_checked_in($slot_name,$returned_slot,$type)) { - &Apache::lonxml::debug("protoctor checked in"); - $slotstatus='CAN_ANSWER'; + &Apache::lonxml::debug("proctor checked in"); + $slotstatus=$status; } my ($is_correct,$got_grade,$checkedin); @@ -325,12 +295,15 @@ sub check_slot_access { return ('WAITING_FOR_GRADE'); } - # no slot is currently open, and has been checked in for this version - # previous slot is therefore CLOSED, so therefore the problem is + # Previously used slot is no longer open, and has been checked in for this version. + # However, the problem is not closed, and potentially, another slot might be + # used to gain access to it to work on it, until the due date is reached, and the + # problem then becomes CLOSED. Therefore return the slotstatus - + # (which will be NOT_IN_SLOT). if (!defined($slot_name) && $checkedin && $type eq 'problem') { - return ('CLOSED',$datemsg); + return ($slotstatus); } if ($slotstatus eq 'NOT_IN_A_SLOT' @@ -373,8 +346,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.')); } @@ -396,13 +369,19 @@ 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."); return($status,$date); } - + if ($env{'form.grade_imsexport'}) { + if (($env{'request.course.id'}) && + (&Apache::lonnet::allowed('mdc',$env{'request.course.id'}))) { + return ('SHOW_ANSWER'); + } + } foreach my $temp ("opendate","duedate","answerdate") { $lastdate = $date; if ($temp eq 'duedate') { @@ -433,7 +412,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; } } @@ -460,28 +439,35 @@ sub check_access { if ( $tries eq '' ) { $tries = '0'; } if ( $maxtries eq '' && $env{'request.state'} ne 'construct') { $maxtries = '2'; } + $Apache::lonhomework::results{'resource.'.$id.'.maxtries'}=$maxtries; if ($maxtries && $tries >= $maxtries) { $status = 'CANNOT_ANSWER'; } # 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.awarded"}==1) || $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); } @@ -507,18 +493,19 @@ sub check_access { 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; } @@ -652,15 +639,16 @@ sub setupheader { sub handle_save_or_undo { my ($request,$problem,$result) = @_; + my $file = &Apache::lonnet::filelocation("",$request->uri); my $filebak =$file.".bak"; my $filetmp =$file.".tmp"; my $error=0; - if ($env{'form.Undo'} eq &mt('undo')) { + if (($env{'form.problemmode'} eq 'undo') || ($env{'form.problemmode'} eq 'undoxml')) { 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]",
@@ -679,13 +667,10 @@ sub handle_save_or_undo {
}
} else {
&Apache::lonnet::correct_line_ends($result);
+
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]",
@@ -696,9 +681,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]",
@@ -713,19 +695,39 @@ sub handle_save_or_undo {
sub analyze_header {
my ($request) = @_;
- my $result =
- &Apache::loncommon::start_page('Analyzing a problem',undef);
+ my $js = &Apache::structuretags::setmode_javascript();
+
+ # Breadcrumbs
+ my $brcrum = [{'href' => &Apache::loncommon::authorspace(),
+ 'text' => 'Construction Space'},
+ {'href' => '',
+ 'text' => 'Problem Testing'},
+ {'href' => '',
+ 'text' => 'Analyzing a problem'}];
+ my $result =
+ &Apache::loncommon::start_page('Analyzing a problem',
+ $js,
+ {'bread_crumbs' => $brcrum,})
+ .&Apache::loncommon::head_subbox(
+ &Apache::loncommon::CSTR_pageheader());
$result .=
&Apache::lonxml::message_location().'
- ';
&Apache::lonxml::add_messages(\$result);
$request->print($result);
@@ -785,42 +787,75 @@ 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('Response').' '.$part.' '.
- &mt('is not analyzable at this time').' '
+ .&mt('Response [_1] is not analyzable at this time.',$part)
+ .' '.&mt('Found no analyzable responses in this problem, currently only Numerical, Formula and String response styles are supported.').' '
+ .&mt('Found no analyzable responses in this problem.'
+ .' Currently only Numerical, Formula and String response styles are supported.')
+ .' '
+ .&mt('Unable to find [_1]',
+ ''.$file.'')
+ .' '
+ .&mt('Unable to find [_1]',
+ ''.$filename.'')
+ ."
'.&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
');
+ $request->print(&Apache::loncommon::end_data_table());
}
} else {
- $request->print(' ');
+ $request->print(''
+ .&Apache::loncommon::end_data_table_header_row()
+ );
my %frequency;
foreach my $answer (sort {$a->[0] <=> $b->[0]} (@{ $overall{$part.'.answer'}[$i] })) {
$frequency{join("\0",@{ $answer })}++;
}
- $request->print(''.&mt('Part').' '.$part);
+ $request->print(&Apache::loncommon::start_data_table()
+ .&Apache::loncommon::start_data_table_header_row()
+ .' '
+ .&mt('Part').' '.$part
+ );
if (scalar(@{ $overall{$part.'.answer'} }) > 1) {
- $request->print(&mt(' Answer [_1]',$i+1));
+ $request->print(' '.&mt('Answer [_1]',$i+1));
}
- $request->print(' ');
+ $request->print(&Apache::loncommon::start_data_table_header_row()
+ .''.&mt('Answer').' '.&mt('Frequency').'
('
- .&mt('click for example').')'.&mt('Answer').' '
+ .''.&mt('Frequency').' '
+ .&Apache::loncommon::end_data_table_header_row()
+ );
foreach my $answer (sort {(split("\0",$a))[0] <=> (split("\0",$b))[0]} (keys(%frequency))) {
- $request->print('
'
+ .'('.&mt('click for example').') ');
+ $request->print(&Apache::loncommon::start_data_table_row()
+ .''.
- join(' ',split("\0",$answer)).
- ' '.$frequency{$answer}.
- ' '
+ .join(' ',split("\0",$answer))
+ .' '
+ .''
+ .''.$frequency{$answer}.''
+ .' '
+ .&Apache::loncommon::end_data_table_row()
+ );
}
- $request->print('
';
if ($cols > 80) { $cols = 80; }
if ($cols < 70) { $cols = 70; }
if ($rows < 20) { $rows = 20; }
+ my $js =
+ &Apache::edit::js_change_detection().
+ &Apache::loncommon::resize_textarea_js().
+ &Apache::structuretags::setmode_javascript().
+ &Apache::lonhtmlcommon::dragmath_js("EditMathPopup");
+
+ # Breadcrumbs
+ my $brcrum = [{'href' => &Apache::loncommon::authorspace(),
+ 'text' => 'Construction Space'},
+ {'href' => '',
+ 'text' => 'Problem Editing'}];
+
my $start_page =
- &Apache::loncommon::start_page(&mt("EditXML [_1]",$file),
- &Apache::edit::js_change_detection(),
- {'no_auto_mt_title' => 1,});
-
- $result.=$start_page.
- &renderpage($request,$file,['no_output_web'],1).
- &Apache::lonxml::message_location().'
- '.&Apache::loncommon::end_page();
&Apache::lonxml::add_messages(\$result);
$request->print($result);
@@ -898,6 +993,7 @@ sub renderpage {
my @targets = @{$targets || [&get_target()]};
&Apache::lonhomework::showhashsubset(\%env,'form.');
&Apache::lonxml::debug("Running targets ".join(':',@targets));
+
my $overall_result;
foreach my $target (@targets) {
# FIXME need to do something intelligent when a problem goes
@@ -918,9 +1014,10 @@ sub renderpage {
$problem='';
my $filename=(split('/',$file))[-1];
my $error =
- " ".&mt('Unable to find [_1]',
- ' '.$filename.'')
- ."";
+ ''.
- &Apache::loncommon::helpLatexCheatsheet("Problem_Editor_XML_Index",
- "Problem Editing Help").
- ' '.
- &Apache::loncommon::help_open_menu(undef,undef,5,'Authoring').
- '
The server encountered an internal error or misconfiguration and was unable to complete your request.
Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.
More information about this error may be available in the server error log.