# The LearningOnline Network with CAPA
# definition of tags that give a structure to a document
#
# $Id: bridgetask.pm,v 1.73 2005/10/24 18:40:21 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#
###
package Apache::bridgetask;
use strict;
use Apache::lonnet;
use Apache::File();
use Apache::lonmenu;
use Apache::lonlocal;
use Apache::lonxml;
use Apache::slotrequest();
use Time::HiRes qw( gettimeofday tv_interval );
BEGIN {
&Apache::lonxml::register('Apache::bridgetask',('Task','IntroParagraph','Dimension','Instance','InstanceText','Criteria','ClosingParagraph'));
}
sub initialize_bridgetask {
# id of current Dimension, 0 means that no dimension is current
# (inside only)
$Apache::bridgetask::dimension='';
# list of all Dimension ids seen
@Apache::bridgetask::dimensionlist=();
# mandatory attribute of all Dimensions seen
%Apache::bridgetask::dimensionmandatory=();
# list of all current Instance ids
@Apache::bridgetask::instance=();
# list of all Instance ids seen in this problem
@Apache::bridgetask::instancelist=();
# key of queud user data that we are currently grading
$Apache::bridgetask::queue_key='';
}
sub proctor_check_auth {
my ($slot_name,$slot)=@_;
my $user=$env{'form.proctorname'};
my $domain=$env{'form.proctordomain'};
my @allowed=split(",",$slot->{'proctor'});
foreach my $possible (@allowed) {
my ($puser,$pdom)=(split('@',$possible));
if ($puser eq $user && $pdom eq $domain) {
my $authenticated=0;
if ( $slot->{'secret'} =~ /\S/ &&
$env{'form.proctorpassword'} eq $slot->{'secret'} ) {
$authenticated=1;
} else {
my $authhost=&Apache::lonnet::authenticate($puser,$env{'form.proctorpassword'},$pdom);
if ($authhost ne 'no_host') {
$authenticated=1;
}
}
if ($authenticated) {
my $version=
$Apache::lonhomework::results{'resource.version'}=
++$Apache::lonhomework::history{'resource.version'};
$Apache::lonhomework::results{"resource.$version.checkedin"}=
$user.'@'.$domain;
$Apache::lonhomework::results{"resource.$version.checkedin.slot"}=
$slot_name;
foreach my $key (keys(%Apache::lonhomework::history)) {
if ($key=~/^resource\.0\./) {
$Apache::lonhomework::results{$key}='';
}
}
return 1;
}
}
}
return 0;
}
sub get_version {
my ($version,$previous);
if ($env{'form.previousversion'} &&
$env{'form.previousversion'} ne 'current' &&
defined($Apache::lonhomework::history{'resource.'.$env{'form.previousversion'}.'.status'})) {
$version=$env{'form.previousversion'};
$previous=1;
} else {
$version=$Apache::lonhomework::history{'resource.version'};
$previous=0;
}
if (wantarray) {
return ($version,$previous);
}
return $version;
}
sub add_previous_version_button {
my ($status)=@_;
my $result;
if ($Apache::lonhomework::history{'resource.version'} eq '') {
return '';
}
if ($Apache::lonhomework::history{'resource.version'} < 2 &&
$status ne 'NEEDS_CHECKIN') {
return '';
}
my $version=&get_version();
if ($env{'form.previousversion'} ne '' &&
$env{'form.previousversion'} eq $version) {
$result.="
".&mt("Showing previous version [_1]",$version).
"
\n";
}
my @to_show;
foreach my $test_version (1..$Apache::lonhomework::history{'resource.version'}) {
if (defined($Apache::lonhomework::history{'resource.'.$test_version.'.status'})) {
push(@to_show,$test_version);
}
}
my $list='\n\n";
$list.='';
$result.='";
return $result;
}
sub add_grading_button {
my (undef,$cid)=&Apache::lonxml::whichuser();
my $cnum=$env{'course.'.$cid.'.num'};
my $cdom=$env{'course.'.$cid.'.domain'};
my %sections;
my $numsections=&Apache::loncommon::get_sections($cdom,$cnum,\%sections);
my $size=5;
if (scalar(keys(%sections)) < 3) {
$size=scalar(keys(%sections))+2;
}
my $sec_select = '\n";
my $result=' ';
$result.='';
if (&Apache::lonnet::allowed('mgq',$env{'request.course.id'})) {
my ($entries,$ready,$locks)=&get_queue_counts('gradingqueue');
$result.='
Specify a section: '.$sec_select.'
';
$result.='
'.&mt("Grading Queue has [_1] entries. [_2] of them are ready to be graded and [_3] of them are currently being graded",$entries,$ready,$locks);
$result.='
'.&mt("Review Queue has [_1] entries. [_2] of them are ready to be graded and [_3] of them are currently being graded",$entries,$ready,$locks);
$result.='
'."\n";
$result.='
'."\n";
}
return $result;
}
sub add_request_another_attempt_button {
my ($text)=@_;
if (!$text) { $text="Request another attempt"; }
my $result;
my $symb=&Apache::lonnet::symbread();
my ($slot_name,$slot)=&Apache::slotrequest::check_for_reservation($symb);
my $action='get_reservation';
if ($slot_name) {
$text="Change reservation.";
$action='change_reservation';
my $description=&Apache::slotrequest::get_description($slot_name,
$slot);
$result.=(< Will be next available: $description
STUFF
}
if ($env{'request.enc'}) { $symb=&Apache::lonenc::encrypted($symb); }
$symb=&Apache::lonnet::escape($symb);
$result.='';
return $result;
}
sub preserve_grade_info {
my $result;
# if we are viewing someone else preserve that info
if (defined $env{'form.grade_symb'}) {
foreach my $field ('symb','courseid','domain','username') {
$result .= ''."\n";
}
}
return $result;
}
sub style {
return (<
STYLE
}
sub show_task {
my ($status,$previous)=@_;
if (!$previous && (
( $status eq 'CLOSED' ) ||
( $status eq 'BANNED') ||
( $status eq 'UNAVAILABLE') ||
( $status eq 'NOT_IN_A_SLOT') ||
( $status eq 'NEEDS_CHECKIN') ||
( $status eq 'WAITING_FOR_GRADE') ||
( $status eq 'INVALID_ACCESS') )) {
return 0;
}
if ($env{'form.donescreen'}) { return 0; }
return 1;
}
sub internal_location {
my ($id)=@_;
return '';
}
sub submission_time_stamp {
my ($symb,$courseid,$udom,$uname)=&Apache::lonxml::whichuser();
my $submissiontime;
my $version=$Apache::lonhomework::history{'resource.version'};
for (my $v=$Apache::lonhomework::history{'version'};$v>0;$v--) {
if (defined($Apache::lonhomework::history{$v.':resource.'.$version.'.0.bridgetask.portfiles'})) {
$submissiontime=$Apache::lonhomework::history{$v.':timestamp'};
}
}
my $result;
if ($submissiontime) {
my $slot_name=$Apache::lonhomework::history{'resource.'.$version.'.checkedin.slot'};
my %slot=&Apache::lonnet::get_slot($slot_name);
my $diff = $slot{'endtime'} - $submissiontime;
my ($color,$when)=('#FF6666','after');
if ($diff > 0) { ($color,$when)=('#336600','before'); }
my $info;
if ($diff%60) { $info=($diff%60).' seconds'; }
$diff=int($diff/60);
if ($diff%60) { $info=($diff%60).' minutes '.$info; }
$diff=int($diff/60);
if ($diff) { $info=$diff.' hours '.$info; }
$result='
'.
&mt('Student submitted [_1] [_2] the deadline.
(Submission was at [_3], end of period was [_4].)',
$info,$when,scalar(localtime($submissiontime)),
scalar(localtime($slot{'endtime'}))).
'
';
}
return $result;
}
sub webgrade_standard_info {
my ($version)=&get_version();
my (undef,undef,$udom,$uname) = &Apache::lonxml::whichuser();
my $file_url = '/uploaded/'.$udom.'/'.$uname.'/portfolio/';
my $file_list="
\n";
foreach my $partial_file (split(',',$Apache::lonhomework::history{"resource.$version.0.bridgetask.portfiles"})) {
my $file=$file_url.$partial_file;
$file=~s|/+|/|g;
&Apache::lonnet::allowuploaded('/adm/bridgetask',$file);
$file_list.='
\n";
my %lt=&Apache::lonlocal::texthash('done' => 'Done',
'stop' => 'Stop',
);
my $result=<
$file_list
INFO
return $result;
}
sub start_Task {
my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
my ($status,$accessmsg,$slot);
if ($target ne 'webgrade') {
&Apache::structuretags::init_problem_globals('Task');
&Apache::structuretags::initialize_storage();
&Apache::lonhomework::showhash(%Apache::lonhomework::history);
}
$Apache::lonhomework::parsing_a_task=1;
#should get back a or the neccesary stuff to start XML/MathML
my ($result,$head_tag_start,$body_tag_start,$form_tag_start)=
&Apache::structuretags::page_start($target,$token,$tagstack,$parstack,$parser,$safeeval);
if ($target eq 'web') {
if ($Apache::lonhomework::modifygrades) {
$body_tag_start.='";
my $symb=&Apache::lonnet::symbread();
if (&Apache::lonnet::allowed('mgq',$env{'request.course.id'})) {
$body_tag_start.='';
}
}
}
if ($target eq 'web' || ($target eq 'grade' && !$env{'form.webgrade'}) || $target eq 'answer' ||
$target eq 'tex') {
my ($version,$previous)=&get_version();
($status,$accessmsg,my $slot_name,$slot) =
&Apache::lonhomework::check_task_access('0');
push(@Apache::inputtags::status,$status);
$Apache::inputtags::slot_name=$slot_name;
my $expression='$external::datestatus="'.$status.'";';
$expression.='$external::gradestatus="'.$Apache::lonhomework::history{"resource.$version.solved"}.'";';
&Apache::run::run($expression,$safeeval);
&Apache::lonxml::debug("Got $status");
$body_tag_start.=&add_previous_version_button($status);
if (!&show_task($status,$previous)) {
my $bodytext=&Apache::lonxml::get_all_text("/task",$parser);
if ( $target eq "web" ) {
$result.= $head_tag_start.''.$body_tag_start;
my $msg;
if ($status eq 'UNAVAILABLE') {
$msg.='
'.&mt('Unable to determine if this resource is open due to network problems. Please try again later.').'
';
} elsif ($status eq 'NOT_IN_A_SLOT') {
$msg.='
'.&mt('You are not currently signed up to work at this time and/or place.').'
';
$msg.=&add_request_another_attempt_button("Sign up for time to work.");
} elsif ($status eq 'NEEDS_CHECKIN') {
$msg.='
'.&mt('Your submission is in the grading queue.').'
';
} elsif ($env{'form.donescreen'}) {
my $title=&Apache::lonnet::gettitle();
my @files=split(',',$Apache::lonhomework::history{'resource.'.$version.'.0.bridgetask.portfiles'});
my $files='
DONESCREEN
} elsif ($status ne 'NOT_YET_VIEWED') {
$msg.='
'.&mt('Not open to be viewed').'
';
}
if ($status eq 'CLOSED' || $status eq 'INVALID_ACCESS') {
$msg.='The problem '.$accessmsg;
}
$result.=$msg.' ';
} elsif ($target eq 'tex') {
$result.='\begin{document}\noindent \vskip 1 mm \begin{minipage}{\textwidth}\vskip 0 mm';
if ($status eq 'UNAVAILABLE') {
$result.=&mt('Unable to determine if this resource is open due to network problems. Please try again later.').'\vskip 0 mm ';
} else {
$result.=&mt('Problem is not open to be viewed. It')." $accessmsg \\vskip 0 mm ";
}
} elsif ($target eq 'grade' && !$env{'form.webgrade'}) {
if ($status eq 'NEEDS_CHECKIN') {
&proctor_check_auth($slot_name,$slot);
}
}
} elsif ($target eq 'web') {
my $name= &Apache::structuretags::get_resource_name($parstack,$safeeval);
$result.="$head_tag_start$name
$body_tag_start \n".&style();
$result.=&preserve_grade_info();
$result.=&internal_location();
$result.=$form_tag_start.
'';
&Apache::lonxml::startredirection();
}
} elsif ( ($target eq 'grade' && $env{'form.webgrade'}) ||
$target eq 'webgrade') {
my $webgrade='yes';
if ($target eq 'webgrade') {
$result.=$head_tag_start.$body_tag_start;
#$result.=' Review'.&show_queue('reviewqueue');
#$result.=' Grade'.&show_queue('gradingqueue');
}
# FIXME Blast! still need to reorg this, need to reshow the
# queue being reviewed once done with the grade pass...
# Hrrm, vaildation pass should perhaps say 'not_locked'
# perhaps do a search if there is a key that is mine and if
# there isn't reshow the queue....
my ($todo,$status_code)=&get_key_todo($target);
if ($todo) {
&setup_env_for_other_user($todo,$safeeval);
my ($symb,$uname,$udom)=&decode_queue_key($todo);
$result.="\n".'
Found '.
&Apache::lonnet::gettitle($symb).' for '.$uname.' at '.$udom.'
';
$form_tag_start.=
'';
$Apache::bridgetask::queue_key=$todo;
&Apache::structuretags::initialize_storage();
&Apache::lonhomework::showhash(%Apache::lonhomework::history);
if ($target eq 'webgrade') {
#$result.=' After -'.&show_queue($env{'form.queue'});
$result.="\n".'
';
if ($status_code eq 'selected') {
$form_tag_start.=
'';
}
}
} else {
if ($target eq 'webgrade') {
$result.="\n";
if ($status_code eq 'stop') {
$result.=''.&mt("Stopped grading.").'';
} elsif ($status_code eq 'lock_failed') {
$result.=''.&mt("Failed to lock the request record.").'';
} elsif ($status_code eq 'unlock') {
$result.=''.&mt("Unlocked the requested record.").'';
$result.=&show_queue($env{'form.queue'},1);
} elsif ($status_code eq 'show_list') {
$result.=&show_queue($env{'form.queue'},1);
} elsif ($status_code eq 'select_user') {
$result.=&select_user();
} else {
$result.=''.&mt("No user to be graded.").'';
}
}
$webgrade='no';
my $bodytext=&Apache::lonxml::get_all_text("/task",$parser);
}
if ($target eq 'webgrade' && defined($env{'form.queue'})) {
if ($webgrade eq 'yes') {
$result.=&submission_time_stamp();
}
$result.=$form_tag_start;
$result.='';
$result.='';
if ($env{'form.regrade'}) {
$result.='';
}
if ($env{'form.chosensections'}) {
my @chosen_sections=
&Apache::loncommon::get_env_multiple('form.chosensections');
foreach my $sec (@chosen_sections) {
$result.='';
}
}
if ($webgrade eq 'yes') { $result.=&webgrade_standard_info(); }
}
} else {
# page_start returned a starting result, delete it if we don't need it
$result = '';
}
return $result;
}
sub get_key_todo {
my ($target)=@_;
my $todo;
if (defined($env{'form.reviewasubmission'})) {
&Apache::lonxml::debug("review a submission....");
$env{'form.queue'}='reviewqueue';
return (undef,'show_list');
}
if (defined($env{'form.reviewagrading'})) {
&Apache::lonxml::debug("review a grading....");
$env{'form.queue'}='gradingqueue';
return (undef,'show_list');
}
if (defined($env{'form.regradeasubmission'})) {
&Apache::lonxml::debug("regrade a grading....");
$env{'form.queue'}='none';
return (undef,'select_user');
}
my $queue=$env{'form.queue'};
if (!defined($queue)) {
$env{'form.queue'}=$queue='gradingqueue';
}
my $gradingkey=&Apache::lonnet::unescape($env{'form.gradingkey'});
if ($env{'form.queue'} eq 'none') {
if (defined($env{'form.gradingkey'})) {
if ($target eq 'webgrade') {
if ($env{'form.stop'}) {
return (undef,'stop');
} elsif ($env{'form.next'}) {
return (undef,'select_user');
}
}
return ($gradingkey,'selected');
} else {
return (undef,'select_user');
}
}
if (defined($env{'form.queue'}) && defined($env{'form.gradingkey'})
&& !defined($env{'form.gradingaction'})
&& $env{'form.queuemode'} eq 'selected') {
my $who=&queue_key_locked($queue,$gradingkey);
my $me=$env{'user.name'}.'@'.$env{'user.domain'};
if ($who eq $me) {
&Apache::lonxml::debug("Found a key was given to me");
return ($gradingkey,'selected');
} else {
return (undef,'show_list');
}
}
if ($target eq 'webgrade' && $env{'form.queuemode'} eq 'selected') {
if ($env{'form.gradingaction'} eq 'resume') {
delete($env{'form.gradingaction'});
&Apache::lonxml::debug("Resuming a key");
return ($gradingkey);
} elsif ($env{'form.gradingaction'} eq 'unlock') {
&Apache::lonxml::debug("Unlocking a key ".
&check_queue_unlock($queue,$gradingkey,1));
return (undef,'unlock');
} elsif ($env{'form.gradingaction'} eq 'select') {
&Apache::lonxml::debug("Locking a key");
if (&lock_key($queue,$gradingkey)) {
&Apache::lonxml::debug("Success $queue");
return ($gradingkey);
}
&Apache::lonxml::debug("Failed $queue");
return (undef,'lock_failed');
}
}
if ($env{'form.queuemode'} ne 'selected') {
# don't get something new from the queue if they hit the stop button
if (!($env{'form.stop'} && $target eq 'webgrade')
&& !$env{'form.gradingaction'}) {
&Apache::lonxml::debug("Getting anew $queue");
return (&get_from_queue($queue));
} else {
return (undef,'stop');
}
}
return (undef,undef)
}
sub end_Task {
my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
my $result='';
my $status=$Apache::inputtags::status['-1'];
my ($version,$previous)=&get_version();
if ($target eq 'grade' || $target eq 'web' || $target eq 'answer' ||
$target eq 'tex') {
if ($target eq 'web' || $target eq 'answer' || $target eq 'tex') {
if ($target eq 'web') {
if (&show_task($status,$previous)) {
$result.=&Apache::lonxml::endredirection();
}
if ($status eq 'CAN_ANSWER' && !$previous &&
!$env{'form.donescreen'}) {
$result.="\n".'
'.&mt('Indicate the files from your portfolio to be evaluated in grading this task.').'
').
"
";
$result.=&Apache::inputtags::gradestatus('0');
$result.='';
$result.=<
DONEBUTTON
}
if (&show_task($status,$previous) &&
$Apache::lonhomework::history{"resource.$version.status"} =~ /^(pass|fail)$/) {
my $bt_status=$Apache::lonhomework::history{"resource.$version.status"};
my $title=&Apache::lonnet::gettitle();
my %slot=&Apache::lonnet::get_slot($Apache::inputtags::slot_name);
my $start_time=
&Apache::lonlocal::locallocaltime($slot{'starttime'});
my $status;
$status.="\n
\n";
if ($bt_status eq 'pass') {
$status.='
You passed the '.$title.' given on '.
$start_time.'.
';
}
if ($bt_status eq 'fail') {
$status.='
You did not pass the '.$title.' given on '.
$start_time.'.
';
if (!$previous) {
$status.=&add_request_another_attempt_button();
}
}
my $man_count=0;
my $opt_count=0;
my $opt_passed=0;
foreach my $dim_id (@Apache::bridgetask::dimensionlist) {
if ($Apache::bridgetask::dimensionmandatory{$dim_id}
eq 'N') {
$opt_count++;
if ($Apache::lonhomework::history{"resource.$version.$dim_id.status"} eq 'pass') {
$opt_passed++;
}
} else {
$man_count++;
}
}
my $opt_req=&Apache::lonxml::get_param('OptionalRequired',
$parstack,$safeeval);
if ($opt_req !~ /\S/) { $opt_req='0'; }
$status.="\n
".&mt('You needed to pass all of the [_1] mandatory components and [_2] of the [_3] optional components on the bridge task.',$man_count,$opt_req,$opt_count)."
\n";
my $internal_location=&internal_location();
$result=~s/\Q$internal_location\E/$status/;
}
}
if ($target eq 'web' || $target eq 'webgrade') {
$result.=&Apache::lonxml::xmlend().'