# The LearningOnline Network with CAPA # Handler for requesting to have slots added to a students record # # $Id: slotrequest.pm,v 1.85 2008/12/21 04:14:39 raeburn 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::slotrequest; use strict; use Apache::Constants qw(:common :http :methods); use Apache::loncommon(); use Apache::lonlocal; use Apache::lonnet; use Apache::lonnavmaps(); use Date::Manip; use lib '/home/httpd/lib/perl/'; use LONCAPA; sub fail { my ($r,$code)=@_; if ($code eq 'not_valid') { $r->print('
'.&mt('Unable to understand what resource you wanted to sign up for.').'
'); } elsif ($code eq 'not_available') { $r->print(''.&mt('No slots are available.').'
'); } elsif ($code eq 'not_allowed') { $r->print(''.&mt('Not allowed to sign up or change reservations at this time.').'
'); } else { $r->print(''.&mt('Failed.').'
'); } &return_link($r); &end_page($r); } sub start_page { my ($r,$title)=@_; $r->print(&Apache::loncommon::start_page($title)); } sub end_page { my ($r)=@_; $r->print(&Apache::loncommon::end_page()); } =pod slot_reservations db - keys are - slotname\0id -> value is an hashref of name -> user@domain of holder timestamp -> timestamp of reservation symb -> symb of resource that it is reserved for =cut sub get_course { (undef,my $courseid)=&Apache::lonnet::whichuser(); my $cdom=$env{'course.'.$courseid.'.domain'}; my $cnum=$env{'course.'.$courseid.'.num'}; return ($cnum,$cdom); } sub get_reservation_ids { my ($slot_name)=@_; my ($cnum,$cdom)=&get_course(); my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum, "^$slot_name\0"); if (&Apache::lonnet::error(%consumed)) { return 'error: Unable to determine current status'; } my ($tmp)=%consumed; if ($tmp=~/^error: 2 / ) { return 0; } return keys(%consumed); } sub space_available { my ($slot_name,$slot)=@_; my $max=$slot->{'maxspace'}; if (!defined($max)) { return 1; } my $consumed=scalar(&get_reservation_ids($slot_name)); if ($consumed < $max) { return 1 } return 0; } sub check_for_reservation { my ($symb,$mode)=@_; my $student = &Apache::lonnet::EXT("resource.0.availablestudent", $symb, $env{'user.domain'}, $env{'user.name'}); my $course = &Apache::lonnet::EXT("resource.0.available", $symb, $env{'user.domain'}, $env{'user.name'}); my @slots = (split(/:/,$student), split(/:/, $course)); &Apache::lonxml::debug(" slot list is ".join(':',@slots)); my ($cnum,$cdom)=&get_course(); my %slots=&Apache::lonnet::get('slots', [@slots], $cdom, $cnum); if (&Apache::lonnet::error($student) || &Apache::lonnet::error($course) || &Apache::lonnet::error(%slots)) { return 'error: Unable to determine current status'; } my @got; foreach my $slot_name (sort { if (ref($slots{$a}) && ref($slots{$b})) { return $slots{$a}{'starttime'} <=> $slots{$b}{'starttime'} } if (ref($slots{$a})) { return -1;} if (ref($slots{$b})) { return 1;} return 0; } @slots) { next if (!defined($slots{$slot_name}) || !ref($slots{$slot_name})); &Apache::lonxml::debug(time." $slot_name ". $slots{$slot_name}->{'starttime'}." -- ". $slots{$slot_name}->{'startreserve'}); if ($slots{$slot_name}->{'endtime'} > time && $slots{$slot_name}->{'startreserve'} < time) { # between start of reservation times and end of slot if ($mode eq 'allslots') { push(@got,$slot_name); } else { return($slot_name, $slots{$slot_name}); } } } if ($mode eq 'allslots' && @got) { return @got; } return (undef,undef); } sub get_consumed_uniqueperiods { my ($slots) = @_; my $navmap=Apache::lonnavmaps::navmap->new; if (!defined($navmap)) { return 'error: Unable to determine current status'; } my @problems = $navmap->retrieveResources(undef, sub { $_[0]->is_problem() },1,0); my %used_slots; foreach my $problem (@problems) { my $symb = $problem->symb(); my $student = &Apache::lonnet::EXT("resource.0.availablestudent", $symb, $env{'user.domain'}, $env{'user.name'}); my $course = &Apache::lonnet::EXT("resource.0.available", $symb, $env{'user.domain'}, $env{'user.name'}); if (&Apache::lonnet::error($student) || &Apache::lonnet::error($course)) { return 'error: Unable to determine current status'; } foreach my $slot (split(/:/,$student), split(/:/, $course)) { $used_slots{$slot}=1; } } if (!ref($slots)) { my ($cnum,$cdom)=&get_course(); my %slots=&Apache::lonnet::get('slots', [keys(%used_slots)], $cdom, $cnum); if (&Apache::lonnet::error(%slots)) { return 'error: Unable to determine current status'; } $slots = \%slots; } my %consumed_uniqueperiods; foreach my $slot_name (keys(%used_slots)) { next if (!defined($slots->{$slot_name}) || !ref($slots->{$slot_name})); next if (!defined($slots->{$slot_name}{'uniqueperiod'}) || !ref($slots->{$slot_name}{'uniqueperiod'})); $consumed_uniqueperiods{$slot_name} = $slots->{$slot_name}{'uniqueperiod'}; } return \%consumed_uniqueperiods; } sub check_for_conflict { my ($symb,$new_slot_name,$new_slot,$slots,$consumed_uniqueperiods)=@_; if (!defined($new_slot->{'uniqueperiod'})) { return undef; } if (!ref($consumed_uniqueperiods)) { $consumed_uniqueperiods = &get_consumed_uniqueperiods($slots); if (ref($consumed_uniqueperiods) eq 'HASH') { if (&Apache::lonnet::error(%$consumed_uniqueperiods)) { return 'error: Unable to determine current status'; } } else { return 'error: Unable to determine current status'; } } my ($new_uniq_start,$new_uniq_end) = @{$new_slot->{'uniqueperiod'}}; foreach my $slot_name (keys(%$consumed_uniqueperiods)) { my ($start,$end)=@{$consumed_uniqueperiods->{$slot_name}}; if (! ($start < $new_uniq_start && $end < $new_uniq_start) || ($start > $new_uniq_end && $end > $new_uniq_end )) { return $slot_name; } } return undef; } sub make_reservation { my ($slot_name,$slot,$symb)=@_; my ($cnum,$cdom)=&get_course(); my $value=&Apache::lonnet::EXT("resource.0.availablestudent",$symb, $env{'user.domain'},$env{'user.name'}); &Apache::lonxml::debug("value is $value".&mt('A network error has occurred.').'
'); return; } if (!%consumed) { $r->print("".&mt('Slot [_1] has no reservations.', $slot_name)."
"); return; } my @names = map { $consumed{$_}{'name'} } (sort(keys(%consumed))); my $names = join(' ',@names); my $msg = &mt('Remove all of [_1] from slot [_2]?',$names,$slot_name); &remove_registration_confirmation($r,$msg,['entry','slotname']); } sub remove_registration_user { my ($r) = @_; my $slot_name = $env{'form.slotname'}; my $name = &Apache::loncommon::plainname($env{'form.uname'}, $env{'form.udom'}); my $title = &Apache::lonnet::gettitle($env{'form.symb'}); my $msg = &mt('Remove [_1] from slot [_2] for [_3]', $name,$slot_name,$title); &remove_registration_confirmation($r,$msg,['uname','udom','slotname', 'entry','symb']); } sub remove_registration_confirmation { my ($r,$msg,$inputs) =@_; my $hidden_input; foreach my $parm (@{$inputs}) { $hidden_input .= '&\'').'" />'."\n"; } my %lt = &Apache::lonlocal::texthash('yes' => 'Yes', 'no' => 'No',); $r->print(<<"END_CONFIRM");$msg
END_CONFIRM } sub release_all_slot { my ($r,$mgr)=@_; my $slot_name = $env{'form.slotname'}; my ($cnum,$cdom)=&get_course(); my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum, "^$slot_name\0"); $r->print(''.&mt('Releasing reservations').'
'); foreach my $entry (sort { $consumed{$a}{'name'} cmp $consumed{$b}{'name'} } (keys(%consumed))) { my ($uname,$udom) = split(':',$consumed{$entry}{'name'}); my ($result,$msg) = &release_reservation($slot_name,$uname,$udom, $consumed{$entry}{'symb'},$mgr); if (!$result) { $r->print(''.&mt($msg).'
'); } else { $r->print("$msg
"); } $r->rflush(); } $r->print(''. &mt('Return to slot list').'
'); &return_link($r); } sub release_slot { my ($r,$symb,$slot_name,$inhibit_return_link,$mgr)=@_; if ($slot_name eq '') { $slot_name=$env{'form.slotname'}; } my ($uname,$udom) = ($env{'user.name'}, $env{'user.domain'}); if ($mgr eq 'F' && defined($env{'form.uname'}) && defined($env{'form.udom'})) { ($uname,$udom) = ($env{'form.uname'}, $env{'form.udom'}); } if ($mgr eq 'F' && defined($env{'form.symb'})) { $symb = &unescape($env{'form.symb'}); } my ($result,$msg) = &release_reservation($slot_name,$uname,$udom,$symb,$mgr); if (!$result) { $r->print(''.&mt($msg).'
'); } else { $r->print("$msg
"); } if ($mgr eq 'F') { $r->print(''. &mt('Return to slot list').'
'); } if (!$inhibit_return_link) { &return_link($r); } return $result; } sub release_reservation { my ($slot_name,$uname,$udom,$symb,$mgr) = @_; my %slot=&Apache::lonnet::get_slot($slot_name); my $description=&get_description($slot_name,\%slot); if ($mgr ne 'F') { if ($slot{'starttime'} < time) { return (0,&mt('Not allowed to release Reservation: [_1], as it has already ended.',$description)); } } # if the reservation symb is for a map get a resource in that map # to check slot parameters on my $navmap=Apache::lonnavmaps::navmap->new; if (!defined($navmap)) { return (0,'error: Unable to determine current status'); } my $passed_resource = $navmap->getBySymb($symb); if ($passed_resource->is_map()) { my ($a_resource) = $navmap->retrieveResources($passed_resource, sub {$_[0]->is_problem()},0,1); $symb = $a_resource->symb(); } # get parameter string, check for existance, rebuild string with the slot my $student = &Apache::lonnet::EXT("resource.0.availablestudent", $symb,$udom,$uname); my @slots = split(/:/,$student); my @new_slots; foreach my $exist_slot (@slots) { if ($exist_slot eq $slot_name) { next; } push(@new_slots,$exist_slot); } my $new_param = join(':',@new_slots); my ($cnum,$cdom)=&get_course(); # get slot reservations, check if user has one, if so remove reservation my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum, "^$slot_name\0"); foreach my $entry (keys(%consumed)) { if ( $consumed{$entry}->{'name'} eq ($uname.':'.$udom) ) { &Apache::lonnet::del('slot_reservations',[$entry], $cdom,$cnum); } } my $use_slots = &Apache::lonnet::EXT("resource.0.useslots", $symb,$udom,$uname); &Apache::lonxml::debug("use_slots is $use_slotsSlot $slot_name marked as deleted.
"); } else { $r->print("An error ($ret) occurse when attempting to delete Slot $slot_name.
"); } } else { if (%consumed) { $r->print("Slot $slot_name has active reservations.
"); } else { $r->print("Slot $slot_name does not exist.
"); } } $r->print(''. &mt('Return to slot list').'
'); &return_link($r); } sub return_link { my ($r) = @_; $r->print(''. &mt('Return to last resource').'
'); } sub get_slot { my ($r,$symb,$conflictable_slot,$inhibit_return_link)=@_; my %slot=&Apache::lonnet::get_slot($env{'form.slotname'}); my $slot_name=&check_for_conflict($symb,$env{'form.slotname'},\%slot); if ($slot_name =~ /^error: (.*)/) { $r->print('' .&mt('An error occurred while attempting to make a reservation. ([_1])',$1) .'
'); &return_link($r); return 0; } if ($slot_name && $slot_name ne $conflictable_slot) { my %slot=&Apache::lonnet::get_slot($slot_name); my $description1=&get_description($slot_name,\%slot); %slot=&Apache::lonnet::get_slot($env{'form.slotname'}); my $description2=&get_description($env{'form.slotname'},\%slot); $r->print("Already have a reservation: $description1
"); if ($slot_name ne $env{'form.slotname'}) { $r->print(<You can either ");
$r->print(<
or
' .&mt('An error occurred while attempting to make a reservation. ([_1])',$1) .'
'); } elsif ($reserved > -1) { $r->print("Success: $description
"); $retvalue = 1; } elsif ($reserved < 0) { $r->print("Already reserved: $description
"); } if (!$inhibit_return_link) { &return_link($r); } return 1; } my %lt=('request'=>"Availibility list", 'try' =>'Try again'); %lt=&Apache::lonlocal::texthash(%lt); my $extra_input; if ($conflictable_slot) { $extra_input=''; } $r->print(<?
or
or STUFF if (!$inhibit_return_link) { &return_link($r); } return 0; } sub allowed_slot { my ($slot_name,$slot,$symb,$slots,$consumed_uniqueperiods)=@_; #already started if ($slot->{'starttime'} < time) { return 0; } &Apache::lonxml::debug("$slot_name starttime good"); #already ended if ($slot->{'endtime'} < time) { return 0; } &Apache::lonxml::debug("$slot_name endtime good"); # not allowed to pick this one if (defined($slot->{'type'}) && $slot->{'type'} ne 'schedulable_student') { return 0; } &Apache::lonxml::debug("$slot_name type good"); # reserve time not yet started if ($slot->{'startreserve'} > time) { return 0; } &Apache::lonxml::debug("$slot_name reserve good"); my $userallowed=0; # its for a different set of users if (defined($slot->{'allowedsections'})) { if (!defined($env{'request.role.sec'}) && grep(/^No section assigned$/, split(',',$slot->{'allowedsections'}))) { $userallowed=1; } if (defined($env{'request.role.sec'}) && grep(/^\Q$env{'request.role.sec'}\E$/, split(',',$slot->{'allowedsections'}))) { $userallowed=1; } if (defined($env{'request.course.groups'})) { my @groups = split(/:/,$env{'request.course.groups'}); my @allowed_sec = split(',',$slot->{'allowedsections'}); foreach my $group (@groups) { if (grep {$_ eq $group} (@allowed_sec)) { $userallowed=1; last; } } } } &Apache::lonxml::debug("$slot_name sections is $userallowed"); # its for a different set of users if (defined($slot->{'allowedusers'}) && grep(/^\Q$env{'user.name'}:$env{'user.domain'}\E$/, split(',',$slot->{'allowedusers'}))) { $userallowed=1; } if (!defined($slot->{'allowedusers'}) && !defined($slot->{'allowedsections'})) { $userallowed=1; } &Apache::lonxml::debug("$slot_name user is $userallowed"); return 0 if (!$userallowed); # not allowed for this resource if (defined($slot->{'symb'}) && $slot->{'symb'} ne $symb) { return 0; } my $conflict = &check_for_conflict($symb,$slot_name,$slot,$slots, $consumed_uniqueperiods); if ($conflict =~ /^error: /) { return 0; } else { if ($slots->{$conflict}{'starttime'} < time) { return 0; } } &Apache::lonxml::debug("$slot_name symb good"); return 1; } sub get_description { my ($slot_name,$slot)=@_; my $description=$slot->{'description'}; if (!defined($description)) { $description=&mt('[_1] From [_2] to [_3]',$slot_name, &Apache::lonlocal::locallocaltime($slot->{'starttime'}), &Apache::lonlocal::locallocaltime($slot->{'endtime'})); } return $description; } sub show_choices { my ($r,$symb)=@_; my ($cnum,$cdom)=&get_course(); my %slots=&Apache::lonnet::dump('slots',$cdom,$cnum); my $consumed_uniqueperiods = &get_consumed_uniqueperiods(\%slots); if (ref($consumed_uniqueperiods) eq 'HASH') { if (&Apache::lonnet::error(%$consumed_uniqueperiods)) { $r->print(''. &mt('An error occurred determining slot availability'). ''); return; } } elsif ($consumed_uniqueperiods =~ /^error: /) { $r->print(''. &mt('An error occurred determining slot availability'). ''); return; } my $available; &Apache::lonxml::debug("Checking Slots"); my @got_slots=&check_for_reservation($symb,'allslots'); if ($got_slots[0] =~ /^error: /) { $r->print(''. &mt('An error occurred determining slot availability'). ''); return; } $r->print('' .&mt('Slot: [_1] has unknown status.',$description) .' | $form | $description | STUFF } if (!$available) { $r->print('
No available times. '. &mt('Return to last resource').' |
'."\n"; $result.=' '. &mt('Specify a file containing the slot definitions.'). ' |
'."\n";
my $upfile_select=&Apache::loncommon::upfile_select_html();
my $ignore=&mt('Ignore First Line');
$result.=< ENDUPFORM $result.=' |
Created $countdone slots\n
"); foreach my $error (@errors) { $r->print("$error
\n"); } &show_table($r,$mgr); return ''; } sub handler { my $r=shift; &Apache::loncommon::content_type($r,'text/html'); &Apache::loncommon::no_cache($r); if ($r->header_only()) { $r->send_http_header(); return OK; } &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}); my $vgr=&Apache::lonnet::allowed('vgr',$env{'request.course.id'}); my $mgr=&Apache::lonnet::allowed('mgr',$env{'request.course.id'}); my $title='Requesting Another Worktime'; if ($env{'form.command'} =~ /^(showslots|uploadstart|csvuploadmap|csvuploadassign)$/ && $vgr eq 'F') { $title = 'Managing Slots'; } &start_page($r,$title); if ($env{'form.command'} eq 'showslots' && $vgr eq 'F') { &show_table($r,$mgr); } elsif ($env{'form.command'} eq 'remove_registration' && $mgr eq 'F') { &remove_registration($r); } elsif ($env{'form.command'} eq 'release' && $mgr eq 'F') { if ($env{'form.entry'} eq 'remove all') { &release_all_slot($r,$mgr); } else { &release_slot($r,undef,undef,undef,$mgr); } } elsif ($env{'form.command'} eq 'delete' && $mgr eq 'F') { &delete_slot($r); } elsif ($env{'form.command'} eq 'uploadstart' && $mgr eq 'F') { &upload_start($r); } elsif ($env{'form.command'} eq 'csvuploadmap' && $mgr eq 'F') { &csv_upload_map($r); } elsif ($env{'form.command'} eq 'csvuploadassign' && $mgr eq 'F') { if ($env{'form.associate'} ne 'Reverse Association') { &csv_upload_assign($r,$mgr); } else { if ( $env{'form.upfile_associate'} ne 'reverse' ) { $env{'form.upfile_associate'} = 'reverse'; } else { $env{'form.upfile_associate'} = 'forward'; } &csv_upload_map($r); } } else { my $symb=&unescape($env{'form.symb'}); if (!defined($symb)) { &fail($r,'not_valid'); return OK; } my (undef,undef,$res)=&Apache::lonnet::decode_symb($symb); my $useslots = &Apache::lonnet::EXT("resource.0.useslots",$symb); if ($useslots ne 'resource' && $useslots ne 'map' && $useslots ne 'map_map') { &fail($r,'not_available'); return OK; } $env{'request.symb'}=$symb; my $type = ($res =~ /\.task$/) ? 'Task' : 'problem'; my ($status) = &Apache::lonhomework::check_slot_access('0',$type); if ($status eq 'CAN_ANSWER' || $status eq 'NEEDS_CHECKIN' || $status eq 'WAITING_FOR_GRADE') { &fail($r,'not_allowed'); return OK; } if ($env{'form.requestattempt'}) { &show_choices($r,$symb); } elsif ($env{'form.command'} eq 'release') { &release_slot($r,$symb); } elsif ($env{'form.command'} eq 'get') { &get_slot($r,$symb); } elsif ($env{'form.command'} eq 'change') { if (&get_slot($r,$symb,$env{'form.releaseslot'},1)) { &release_slot($r,$symb,$env{'form.releaseslot'}); } } else { $r->print("Unknown command: ".$env{'form.command'}."
"); } } &end_page($r); return OK; } 1; __END__