--- loncom/homework/lonhomework.pm 2016/05/13 22:48:09 1.361
+++ loncom/homework/lonhomework.pm 2023/06/04 05:27:28 1.377
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Homework handler
#
-# $Id: lonhomework.pm,v 1.361 2016/05/13 22:48:09 raeburn Exp $
+# $Id: lonhomework.pm,v 1.377 2023/06/04 05:27:28 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -50,16 +50,22 @@ use Apache::chemresponse();
use Apache::functionplotresponse();
use Apache::drawimage();
use Apache::loncapamath();
+use Apache::loncourseuser();
+use Apache::grades();
use Apache::Constants qw(:common);
use Apache::loncommon();
use Apache::lonparmset();
+use Apache::lonnavmaps();
use Apache::lonlocal;
+use LONCAPA qw(:DEFAULT :match);
+use LONCAPA::ltiutils();
use Time::HiRes qw( gettimeofday tv_interval );
use HTML::Entities();
use File::Copy();
# FIXME - improve commenting
+my $registered_cleanup;
BEGIN {
&Apache::lonxml::register_insert();
@@ -190,7 +196,7 @@ sub proctor_checked_in {
if ($type eq 'Task') {
my $version=$Apache::lonhomework::history{'resource.0.version'};
$key ="resource.$version.0.checkedin";
- } elsif ($type eq 'problem') {
+ } elsif (($type eq 'problem') || ($type eq 'tool')) {
$key ='resource.0.checkedin';
}
# backward compatability, used to be username@domain,
@@ -205,16 +211,15 @@ sub proctor_checked_in {
return 1;
}
}
-
return 0;
}
sub check_slot_access {
- my ($id,$type,$symb)=@_;
+ my ($id,$type,$symb,$partlist)=@_;
# does it pass normal muster
my ($status,$datemsg)=&check_access($id,$symb);
-
+
my $useslots = &Apache::lonnet::EXT("resource.0.useslots",$symb);
if ($useslots ne 'resource' && $useslots ne 'map'
&& $useslots ne 'map_map') {
@@ -228,16 +233,19 @@ sub check_slot_access {
$checkin = "resource.$version.0.checkedin";
}
my $checkedin = $Apache::lonhomework::history{$checkin};
- my ($returned_slot,$slot_name,$checkinslot,$ipused,$blockip,$now,$ip);
+ my ($returned_slot,$slot_name,$checkinslot,$ipused,$blockip,$now,$ip,
+ $consumed_uniq);
$now = time;
- $ip=$env{'request.host'} || $ENV{'REMOTE_ADDR'};
+ $ip=$ENV{'REMOTE_ADDR'} || $env{'request.host'};
if ($checkedin) {
$checkinslot = $Apache::lonhomework::history{"$checkin.slot"};
my %slot=&Apache::lonnet::get_slot($checkinslot);
+ $consumed_uniq = $slot{'uniqueperiod'};
if ($slot{'iptied'}) {
$ipused = $Apache::lonhomework::history{"$checkin.ip"};
- unless (($ip ne '') && ($ipused eq $ip)) {
+ unless (($ip ne '') &&
+ (($ipused eq $ip) || ($ENV{'REMOTE_ADDR'} eq '127.0.0.1'))) {
$blockip = $slot{'iptied'};
$slot_name = $checkinslot;
$returned_slot = \%slot;
@@ -251,7 +259,7 @@ sub check_slot_access {
} else {
return ($status,$datemsg);
}
- }
+ }
if ($status eq 'CLOSED' ||
$status eq 'INVALID_ACCESS' ||
@@ -261,7 +269,7 @@ sub check_slot_access {
if ($env{'request.state'} eq "construct") {
return ($status,$datemsg);
}
-
+
if ($type eq 'Task') {
if ($checkedin &&
$Apache::lonhomework::history{"resource.$version.0.status"} eq 'pass') {
@@ -271,6 +279,12 @@ sub check_slot_access {
return ('SHOW_ANSWER');
}
}
+ } elsif (($type eq 'problem') &&
+ ($Apache::lonhomework::browse eq 'F') &&
+ ($ENV{'REMOTE_ADDR'} eq '127.0.0.1') &&
+ ($env{'form.grade_courseid'} eq $env{'request.course.id'}) &&
+ (&Apache::lonnet::allowed('mgr',$env{'request.course.id'}))) {
+ return ($status,$datemsg);
}
my $availablestudent = &Apache::lonnet::EXT("resource.0.availablestudent",$symb);
@@ -348,10 +362,49 @@ sub check_slot_access {
$is_correct =
($Apache::lonhomework::history{"resource.$version.0.status"} eq 'pass'
|| $Apache::lonhomework::history{"resource.0.solved"} =~ /^correct_/ );
- } elsif ($type eq 'problem') {
- $got_grade = 1;
- $is_correct =
- ($Apache::lonhomework::history{"resource.0.solved"} =~/^correct_/);
+ } elsif (($type eq 'problem') || ($type eq 'tool')) {
+ if ((ref($partlist) eq 'ARRAY') && (@{$partlist} > 0)) {
+ my ($numcorrect,$numgraded) = (0,0);
+ foreach my $part (@{$partlist}) {
+ my $currtries = $Apache::lonhomework::history{"resource.$part.tries"};
+ my $maxtries = &Apache::lonnet::EXT("resource.$part.maxtries",$symb);
+ my $probstatus = &Apache::structuretags::get_problem_status($part);
+ my $earlyout;
+ unless (($probstatus eq 'no') ||
+ ($probstatus eq 'no_feedback_ever')) {
+ if ($Apache::lonhomework::history{"resource.$part.solved"} =~/^correct_/) {
+ $numcorrect ++;
+ } else {
+ $earlyout = 1;
+ }
+ }
+ if ($currtries == $maxtries) {
+ $earlyout = 1;
+ } else {
+ $numgraded ++;
+ }
+ last if ($earlyout);
+ }
+ my $numparts = scalar(@{$partlist});
+ if ($numparts == $numcorrect) {
+ $is_correct = 1;
+ }
+ if ($numparts == $numgraded) {
+ $got_grade = 1;
+ }
+ } else {
+ my $currtries = $Apache::lonhomework::history{"resource.0.tries"};
+ my $maxtries = &Apache::lonnet::EXT("resource.0.maxtries",$symb);
+ my $probstatus = &Apache::structuretags::get_problem_status('0');
+ unless (($probstatus eq 'no') ||
+ ($probstatus eq 'no_feedback_ever')) {
+ $is_correct =
+ ($Apache::lonhomework::history{"resource.0.solved"} =~/^correct_/);
+ }
+ unless (($currtries == $maxtries) || ($is_correct)) {
+ $got_grade = 1;
+ }
+ }
}
&Apache::lonxml::debug(" slot is $slotstatus checkedin ($checkedin) got_grade ($got_grade) is_correct ($is_correct)");
@@ -369,34 +422,12 @@ sub check_slot_access {
# 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 one of: NOT_IN_A_SLOT, RESERVABLE, RESERVABLE_LATER, or NOTRESERVABLE).
- if (!defined($slot_name) && $type eq 'problem') {
+
+ if (!defined($slot_name) && (($type eq 'problem') || ($type eq 'tool'))) {
if ($slotstatus eq 'NOT_IN_A_SLOT') {
if (!$num_usable_slots) {
- if ($env{'request.course.id'}) {
- my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
- my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
- unless ($symb) {
- ($symb)=&Apache::lonnet::whichuser();
- }
- $slotstatus = 'NOTRESERVABLE';
- my ($reservable_now_order,$reservable_now,$reservable_future_order,
- $reservable_future) =
- &Apache::loncommon::get_future_slots($cnum,$cdom,$now,$symb);
- if ((ref($reservable_now_order) eq 'ARRAY') && (ref($reservable_now) eq 'HASH')) {
- if (@{$reservable_now_order} > 0) {
- $slotstatus = 'RESERVABLE';
- $datemsg = $reservable_now->{$reservable_now_order->[-1]}{'endreserve'};
- }
- }
- unless ($slotstatus eq 'RESERVABLE') {
- if ((ref($reservable_future_order) eq 'ARRAY') && (ref($reservable_future) eq 'HASH')) {
- if (@{$reservable_future_order} > 0) {
- $slotstatus = 'RESERVABLE_LATER';
- $datemsg = $reservable_future->{$reservable_future_order->[0]}{'startreserve'};
- }
- }
- }
- }
+ ($slotstatus,$datemsg) = &check_reservable_slot($slotstatus,$symb,$now,$checkedin,
+ $consumed_uniq);
}
}
return ($slotstatus,$datemsg);
@@ -418,7 +449,7 @@ sub check_slot_access {
}
if (($is_correct) && ($blockip ne 'answer')) {
- if ($type eq 'problem') {
+ if (($type eq 'problem') || ($type eq 'tool')) {
return ($status);
}
return ('SHOW_ANSWER');
@@ -432,6 +463,81 @@ sub check_slot_access {
return ($slotstatus,$datemsg,$slot_name,$returned_slot,$ipused);
}
+sub check_reservable_slot {
+ my ($slotstatus,$symb,$now,$checkedin,$consumed_uniq) = @_;
+ my $datemsg;
+ if ($slotstatus eq 'NOT_IN_A_SLOT') {
+ if ($env{'request.course.id'}) {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ unless ($symb) {
+ ($symb)=&Apache::lonnet::whichuser();
+ }
+ $slotstatus = 'NOTRESERVABLE';
+ my ($reservable_now_order,$reservable_now,$reservable_future_order,
+ $reservable_future) =
+ &Apache::loncommon::get_future_slots($cnum,$cdom,$now,$symb);
+ if ((ref($reservable_now_order) eq 'ARRAY') && (ref($reservable_now) eq 'HASH')) {
+ if (@{$reservable_now_order} > 0) {
+ if ((!$checkedin) || (ref($consumed_uniq) ne 'ARRAY')) {
+ $slotstatus = 'RESERVABLE';
+ $datemsg = $reservable_now->{$reservable_now_order->[-1]}{'endreserve'};
+ } else {
+ my ($uniqstart,$uniqend,$useslot);
+ if (ref($consumed_uniq) eq 'ARRAY') {
+ ($uniqstart,$uniqend)=@{$consumed_uniq};
+ }
+ foreach my $slot (reverse(@{$reservable_now_order})) {
+ if ($reservable_now->{$slot}{'uniqueperiod'} =~ /^(\d+)\,(\d+)$/) {
+ my ($new_uniq_start,$new_uniq_end) = ($1,$2);
+ next if (!
+ ($uniqstart < $new_uniq_start && $uniqend < $new_uniq_start) ||
+ ($uniqstart > $new_uniq_end && $uniqend > $new_uniq_end ));
+ }
+ $useslot = $slot;
+ last;
+ }
+ if ($useslot) {
+ $slotstatus = 'RESERVABLE';
+ $datemsg = $reservable_now->{$useslot}{'endreserve'};
+ }
+ }
+ }
+ }
+ unless ($slotstatus eq 'RESERVABLE') {
+ if ((ref($reservable_future_order) eq 'ARRAY') && (ref($reservable_future) eq 'HASH')) {
+ if (@{$reservable_future_order} > 0) {
+ if ((!$checkedin) || (ref($consumed_uniq) ne 'ARRAY')) {
+ $slotstatus = 'RESERVABLE_LATER';
+ $datemsg = $reservable_future->{$reservable_future_order->[0]}{'startreserve'};
+ } else {
+ my ($uniqstart,$uniqend,$useslot);
+ if (ref($consumed_uniq) eq 'ARRAY') {
+ ($uniqstart,$uniqend)=@{$consumed_uniq};
+ }
+ foreach my $slot (@{$reservable_future_order}) {
+ if ($reservable_future->{$slot}{'uniqueperiod'} =~ /^(\d+),(\d+)$/) {
+ my ($new_uniq_start,$new_uniq_end) = ($1,$2);
+ next if (!
+ ($uniqstart < $new_uniq_start && $uniqend < $new_uniq_start) ||
+ ($uniqstart > $new_uniq_end && $uniqend > $new_uniq_end ));
+ }
+ $useslot = $slot;
+ last;
+ }
+ if ($useslot) {
+ $slotstatus = 'RESERVABLE_LATER';
+ $datemsg = $reservable_future->{$useslot}{'startreserve'};
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return ($slotstatus,$datemsg);
+}
+
# JB, 9/24/2002: Any changes in this function may require a change
# in lonnavmaps::resource::getDateStatus.
sub check_access {
@@ -561,15 +667,16 @@ sub check_access {
if ($status eq 'CAN_ANSWER' || $status eq 'CANNOT_ANSWER') {
my @interval=&Apache::lonnet::EXT("resource.$id.interval",$symb);
&Apache::lonxml::debug("looking for interval @interval");
- if ($interval[0]) {
+ if ($interval[0]=~ /^\d+/) {
my $first_access=&Apache::lonnet::get_first_access($interval[1],$symb);
&Apache::lonxml::debug("looking for accesstime $first_access");
if (!$first_access) {
$status='NOT_YET_VIEWED';
my $due_date = &due_date($id,$symb);
my $seconds_left = $due_date - time;
- if ($seconds_left > $interval[0] || $due_date eq '') {
- $seconds_left = $interval[0];
+ my ($timelimit) = ($interval[0] =~ /^(\d+)/);
+ if ($seconds_left > $timelimit || $due_date eq '') {
+ $seconds_left = $timelimit;
}
$datemsg=&seconds_to_human_length($seconds_left);
}
@@ -604,7 +711,8 @@ sub due_date {
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)) {
- my $interval = $first_access+$interval[0];
+ my ($timelimit) = ($interval[0] =~ /^(\d+)/);
+ my $interval = $first_access+$timelimit;
$date = (!$due_date || $interval < $due_date) ? $interval
: $due_date;
} else {
@@ -689,6 +797,9 @@ sub setuppermissions {
$env{'request.course.sec'} !~ /^\s*$/) {
$viewgrades = &Apache::lonnet::allowed('vgr',$env{'request.course.id'}.
'/'.$env{'request.course.sec'});
+ if ($viewgrades) {
+ $Apache::lonhomework::viewgradessec = $env{'request.course.sec'};
+ }
}
$Apache::lonhomework::viewgrades = $viewgrades;
@@ -706,6 +817,9 @@ sub setuppermissions {
$modifygrades =
&Apache::lonnet::allowed('mgr',$env{'request.course.id'}.
'/'.$env{'request.course.sec'});
+ if ($modifygrades) {
+ $Apache::lonhomework::modifygradessec = $env{'request.course.sec'};
+ }
}
$Apache::lonhomework::modifygrades = $modifygrades;
@@ -724,7 +838,9 @@ sub setuppermissions {
sub unset_permissions {
undef($Apache::lonhomework::queuegrade);
undef($Apache::lonhomework::modifygrades);
+ undef($Apache::lonhomework::modifygradessec);
undef($Apache::lonhomework::viewgrades);
+ undef($Apache::lonhomework::viewgradessec);
undef($Apache::lonhomework::browse);
}
@@ -1095,27 +1211,27 @@ sub editxmlmode {
unless ($env{'environment.nocodemirror'}) {
# dropdown menus
- $result .= Apache::lonmenu::create_submenu("#", "",
+ $result .= Apache::lonmenu::create_submenu("#", "",
&mt("Problem Templates"), template_dropdown_datastructure());
- $result .= Apache::lonmenu::create_submenu("#", "",
+ $result .= Apache::lonmenu::create_submenu("#", "",
&mt("Response Types"), responseblock_dropdown_datastructure());
- $result .= Apache::lonmenu::create_submenu("#", "",
+ $result .= Apache::lonmenu::create_submenu("#", "",
&mt("Conditional Blocks"), conditional_scripting_datastructure());
- $result .= Apache::lonmenu::create_submenu("#", "",
+ $result .= Apache::lonmenu::create_submenu("#", "",
&mt("Miscellaneous"), misc_datastructure());
}
- $result .= Apache::lonmenu::create_submenu("#", "",
+ $result .= Apache::lonmenu::create_submenu("#", "",
&mt("Help") . ' ',
+ '" style="vertical-align:text-bottom; height: auto; margin:0; "/>',
helpmenu_datastructure(),"");
$result.="";
-
- $result .= '' .
+
+ $result .= '' .
&Apache::lonxml::message_location() .
&Apache::loncommon::xmleditor_js() .
'