Annotation of loncom/automation/Autocreate.pl, revision 1.12
1.1 raeburn 1: #!/usr/bin/perl
2: #
3: # Automated Course Creation script
4: #
1.12 ! raeburn 5: # $Id: Autocreate.pl,v 1.11 2010/01/31 18:06:10 raeburn Exp $
1.9 raeburn 6: #
1.1 raeburn 7: # Copyright Michigan State University Board of Trustees
8: #
9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
10: #
11: # LON-CAPA is free software; you can redistribute it and/or modify
12: # it under the terms of the GNU General Public License as published by
13: # the Free Software Foundation; either version 2 of the License, or
14: # (at your option) any later version.
15: #
16: # LON-CAPA is distributed in the hope that it will be useful,
17: # but WITHOUT ANY WARRANTY; without even the implied warranty of
18: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19: # GNU General Public License for more details.
20: #
21: # You should have received a copy of the GNU General Public License
22: # along with LON-CAPA; if not, write to the Free Software
23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24: #
25: # /home/httpd/html/adm/gpl.txt
26: #
27: # http://www.lon-capa.org/
28: #
1.12 ! raeburn 29: # Run as www. Called from an entry in /etc/cron.d/loncapa
! 30: # either with command line args:
1.1 raeburn 31: #
32: # www /home/httpd/perl/Autocreate.pl $dom $uname:$udom
33: #
1.12 ! raeburn 34: # where $dom is the name of the course domain, $uname and $udom are the
! 35: # username and domain of a Domain Coordinator in the domain.
! 36: #
! 37: # or without args (default) controlled by domain configuration settings:
! 38: #
! 39: # www /home/httpd/perl/Autocreate.pl
1.1 raeburn 40: #
41: use strict;
42: use lib '/home/httpd/lib/perl';
43: use Apache::lonnet;
1.6 raeburn 44: use Apache::lonlocal;
1.12 ! raeburn 45: use Apache::loncoursequeueadmin;
1.1 raeburn 46: use LONCAPA::batchcreatecourse;
47: use LONCAPA::Configuration;
1.11 raeburn 48: use LONCAPA();
1.1 raeburn 49:
50: my $perlvarref = &LONCAPA::Configuration::read_conf('loncapa.conf');
51: my $logfile = $$perlvarref{'lonDaemons'}.'/logs/autocreate.log';
1.12 ! raeburn 52: my @machinedoms = sort(&Apache::lonnet::current_machine_domains());
! 53: my @ids=&Apache::lonnet::current_machine_ids();
! 54: my (@libids,@domains);
! 55: foreach my $id (@ids) {
! 56: if (&Apache::lonnet::is_library($id)) {
! 57: push(@libids,$id);
! 58: }
! 59: }
! 60: exit if (!@libids);
! 61: foreach my $dom (@machinedoms) {
! 62: my $primary = &Apache::lonnet::domain($dom,'primary');
! 63: if (grep(/^\Q$primary\E$/,@libids)) {
! 64: unless (grep(/^\Q$dom\E$/,@domains)) {
! 65: push(@domains,$dom);
! 66: }
! 67: }
! 68: }
! 69: exit if (!@domains);
1.1 raeburn 70: open (my $fh,">>$logfile");
71: print $fh "********************\n".localtime(time)." Autocreation messages start --\n";
72: my $wwwid=getpwnam('www');
73: if ($wwwid!=$<) {
74: my $emailto=$$perlvarref{'lonAdmEMail'};
75: my $subj="LON: $$perlvarref{'lonHostID'} User ID mismatch";
76: my $requestmail = "To: $emailto\n";
1.12 ! raeburn 77: $requestmail .=
1.1 raeburn 78: "Subject: LON: $$perlvarref{'lonHostID'} User ID mismatch\n".
1.12 ! raeburn 79: "User ID mismatch. Autocreate.pl must be run as user www\n";
1.1 raeburn 80: if ($emailto =~ /^[^\@]+\@[^\@]+$/) {
81: if (open(MAIL, "|/usr/lib/sendmail -oi -t -odb")) {
82: print MAIL $requestmail;
83: close(MAIL);
84: print $fh "Autocreate.pl must be run as user www\n\n";
85: } else {
1.12 ! raeburn 86: print $fh "Could not send notification e-mail to $emailto\n\n";
1.1 raeburn 87: }
88: } else {
89: print $fh "Notification e-mail address for Administrator is not a valid e-mail address\n\n";
90: }
91: close($fh);
92: exit;
93: }
1.12 ! raeburn 94: if (@ARGV) {
! 95: # check if specified course domain is a domain hosted on this library server.
! 96: if (!grep(/^\Q$ARGV[0]\E$/,@domains)) {
! 97: print $fh "The domain you supplied is not a valid domain for this server\n";
! 98: close($fh);
! 99: exit;
! 100: } elsif (@ARGV < 2) {
! 101: print $fh "usage: ./Autocreate <coursedomain username:domain>.\nPlease provide the username and domain of a Domain Coordinator, if you provide a coursedomain.\nThe script can also be called without any arguments, in which case domain configuration data for domains hosted on this server will be used.\n";
! 102: close($fh);
! 103: exit;
! 104: } else {
! 105: my $defdom = $ARGV[0];
! 106: my ($dcname,$dcdom) = ($ARGV[1] =~ /^([^:]+):([^:]+)$/);
! 107: # check if user is an active domain coordinator.
! 108: if (!&check_activedc($dcdom,$dcname,$defdom)) {
! 109: print $fh "The username you supplied for domain $defdom does not have an active domain coordinator role in the domain\n\n";
! 110: close($fh);
! 111: exit;
! 112: }
! 113: my $output = &process_xml($fh,$defdom,$dcname,$dcdom);
! 114: print $output;
! 115: }
! 116: } else {
! 117: my $reqsnamespace = 'courserequestqueue';
! 118: my @courseroles = ('cc','in','ta','ep','ad','st');
! 119: my %longroles;
! 120: foreach my $role (@courseroles) {
! 121: $longroles{$role}=&Apache::lonnet::plaintext($role);
! 122: }
! 123: my @permissions = ('mau','ccc','cin','cta','cep','ccr','cst');
! 124: my %permissionflags = ();
! 125: &set_permissions(\%permissionflags,\@permissions);
! 126: foreach my $dom (@domains) {
! 127: my %domconfig = &Apache::lonnet::get_dom('configuration',
! 128: ['autocreate'],$dom);
! 129: #only run if configured to
! 130: my $xml_update = 0;
! 131: my $settings;
! 132: if (ref($domconfig{'autocreate'}) eq 'HASH') {
! 133: $settings = $domconfig{'autocreate'};
! 134: if ($settings->{'xml'}) {
! 135: if ($settings->{'xmldc'}) {
! 136: my ($dcname,$dcdom) = split(':',$settings->{'xmldc'});
! 137: $env{'user.name'} = $dcname;
! 138: $env{'user.domain'} = $dcdom;
! 139: $env{'request.role.domain'} = $dom;
! 140: if (!&check_activedc($dcdom,$dcname,$dom)) {
! 141: print $fh "Autocreate.pl in domain $dom configured to run under the auspices of a user without an active domain coordinator role in the domain - course creation will be skipped.\n\n";
! 142: next;
! 143: } else {
! 144: &process_xml($fh,$dom,$dcname,$dcdom);
! 145: }
! 146: } else {
! 147: print $fh "Autocreate.pl in domain $dom - no specified DC under whose identity course creation will occur - domain skipped.\n\n";
! 148: }
! 149: }
! 150: if ($settings->{'req'}) {
! 151: my %domdefs = &Apache::lonnet::get_domain_defaults($dom);
! 152: &process_official_reqs($fh,$dom,$reqsnamespace,\%longroles,\%domdefs);
! 153: }
! 154: }
! 155: }
! 156: &unset_permissions(\%permissionflags);
! 157: }
! 158: print $fh "-- ".localtime(time)." Autocreation messages end\n*******************\n\n";
! 159: close($fh);
! 160:
! 161:
! 162: sub process_xml {
! 163: my ($fh,$dom,$dcname,$dcdom) = @_;
! 164: $env{'user.name'} = $dcname;
! 165: $env{'user.domain'} = $dcdom;
! 166: $env{'request.role.domain'} = $dom;
1.1 raeburn 167:
1.12 ! raeburn 168: # Initialize language handler
! 169: &Apache::lonlocal::get_language_handle();
! 170:
! 171: my $batchdir = $$perlvarref{'lonDaemons'}.'/tmp/addcourse/'.$dom.'/auto';
! 172: opendir(DIR,"$batchdir/pending");
! 173: my @requests = grep(!/^\.\.?$/,readdir(DIR));
! 174: closedir(DIR);
! 175: my %courseids = ();
! 176: print $fh "Sending to batch - auto,$dom,$dcname,$dcdom ".join(":",@requests)."\n";
! 177: my ($result,$logmsg) = &LONCAPA::batchcreatecourse::create_courses(\@requests,\%courseids,'auto',$dom,$dcname,$dcdom);
1.8 raeburn 178: my $outcome;
1.7 raeburn 179: if ($result ne '') {
1.8 raeburn 180: $outcome = $result."\n";
1.7 raeburn 181: }
182: if ($logmsg ne '') {
1.8 raeburn 183: $outcome .= $logmsg."\n";
1.7 raeburn 184: }
1.8 raeburn 185: print $fh $outcome;
1.1 raeburn 186:
1.8 raeburn 187: my $output;
1.1 raeburn 188: # Copy requests from pending directory to processed directory and unlink.
1.8 raeburn 189: foreach my $request (@requests) {
1.1 raeburn 190: if ((-e "$batchdir/pending/$request") && $request !~ /\.\./ && $request ne '' &&$request ne './') {
191: open(FILE,"<$batchdir/pending/$request");
192: my @buffer = <FILE>;
193: close(FILE);
1.3 raeburn 194: if (!-e "$batchdir/processed") {
195: mkdir("$batchdir/processed", 0755);
196: }
1.1 raeburn 197: open(FILE,">$batchdir/processed/$request");
198: print FILE @buffer;
199: close(FILE);
200: if (-e "$batchdir/processed/$request") {
201: unlink("$batchdir/pending/$request");
202: }
203: }
204: }
1.12 ! raeburn 205: foreach my $key (sort(keys(%courseids))) {
1.3 raeburn 206: print $fh "created course: $key - $courseids{$key}\n";
1.11 raeburn 207: my $newcourse = &LONCAPA::escape($key.':'.$courseids{$key});
1.12 ! raeburn 208: $output .= $newcourse.':';
1.1 raeburn 209: }
1.3 raeburn 210: $output =~ s/:$//;
1.4 raeburn 211: delete($env{'user.name'});
212: delete($env{'user.domain'});
1.6 raeburn 213: delete($env{'request.role.domain'});
1.12 ! raeburn 214: return $output;
! 215: }
! 216:
! 217: sub process_official_reqs {
! 218: my ($fh,$dom,$reqsnamespace,$longroles,$domdefs) = @_;
! 219: my %newcids;
! 220: my %requesthash =
! 221: &Apache::lonnet::dump_dom($reqsnamespace,$dom,undef,'_pending');
! 222: foreach my $key (keys(%requesthash)) {
! 223: my ($cnum,$status) = split('_',$key);
! 224: next if (&Apache::lonnet::homeserver($cnum,$dom) ne 'no_host');
! 225: if (ref($requesthash{$key}) eq 'HASH') {
! 226: my $ownername = $requesthash{$key}{'ownername'};
! 227: my $ownerdom = $requesthash{$key}{'ownerdom'};
! 228: next if (&Apache::lonnet::homeserver($ownername,$ownerdom) eq 'no_host');
! 229: my $inststatus;
! 230: my %userenv =
! 231: &Apache::lonnet::get('environment',['inststatus'],
! 232: $ownerdom,$ownername);
! 233: my ($tmp) = keys(%userenv);
! 234: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
! 235: $inststatus = $userenv{'inststatus'};
! 236: } else {
! 237: undef(%userenv);
! 238: }
! 239: my $reqkey = $dom.'_'.$cnum;
! 240: my %history = &Apache::lonnet::restore($reqkey,'courserequests',
! 241: $ownerdom,$ownername);
! 242: if (ref($history{'details'}) eq 'HASH') {
! 243: my $instcode = $history{'details'}{'instcode'};
! 244: my $crstype = $history{'details'}{'crstype'};
! 245: my $reqtime = $history{'details'}{'reqtime'};
! 246: my $cdescr = $history{'details'}{'cdescr'};
! 247: my @currsec;
! 248: my $sections = $history{'details'}{'sections'};
! 249: if (ref($sections) eq 'HASH') {
! 250: foreach my $i (sort(keys(%{$sections}))) {
! 251: if (ref($sections->{$i}) eq 'HASH') {
! 252: my $sec = $sections->{$i}{'inst'};
! 253: if (!grep(/^\Q$sec\E$/,@currsec)) {
! 254: push(@currsec,$sec);
! 255: }
! 256: }
! 257: }
! 258: }
! 259: my $instseclist = join(',',@currsec);
! 260: my ($validationchk,$disposition,$reqstatus,$message,
! 261: $validation,$validationerror);
! 262: $validationchk =
! 263: &Apache::lonnet::auto_courserequest_validation($dom,
! 264: $ownername.':'.$ownerdom,$crstype,$inststatus,
! 265: $instcode,$instseclist);
! 266: if ($validationchk =~ /:/) {
! 267: ($validation,$message) = split(':',$validationchk);
! 268: } else {
! 269: $validation = $validationchk;
! 270: }
! 271: if ($validation =~ /^error(.*)$/) {
! 272: $disposition = 'approval';
! 273: $validationerror = $1;
! 274: } else {
! 275: $disposition = $validation;
! 276: }
! 277: $reqstatus = $disposition;
! 278: if ($disposition eq 'process') {
! 279: my ($logmsg,$newusermsg,$addresult,$enrollcount,$response,$keysmsg);
! 280: my $result = &Apache::loncoursequeueadmin::course_creation($dom,$cnum,'domain',$history{'details'},\$logmsg,\$newusermsg,\$addresult,\$enrollcount,\$response,\$keysmsg,$domdefs,$longroles);
! 281: if ($result eq 'created') {
! 282: $disposition = 'created';
! 283: $reqstatus = 'created';
! 284: push(@{$newcids{$instcode}},$dom.'_'.$cnum);
! 285: }
! 286: } elsif ($disposition eq 'rejected') {
! 287: print $fh &mt('Queued course request for [_1] submitted by [_2] with status [_3] rejected when validating',$instcode,$ownername.':'.$ownerdom,$inststatus);
! 288: } elsif ($disposition eq 'approval') {
! 289: print $fh &mt('Queued course request for [_1] submitted by [_2] with status [_3] switched to "approval by DC" because of validation error: [_4].',$instcode,$ownername.':'.$ownerdom,$inststatus,$validationerror);
! 290:
! 291: my $requestid = $cnum.'_'.$disposition;
! 292: my $request = {
! 293: $requestid => {
! 294: timestamp => $reqtime,
! 295: crstype => $crstype,
! 296: ownername => $ownername,
! 297: ownerdom => $ownerdom,
! 298: description => $cdescr,
! 299: },
! 300: };
! 301: my $putresult = &Apache::lonnet::newput_dom('courserequestqueue',$request,$dom);
! 302: unless ($putresult eq 'ok') {
! 303: print $fh &mt("An error occurred saving the modified course request for [_1] submitted by [_2] in the domain's courserequestqueue.db.",$instcode,$ownername.':'.$ownerdom);
! 304: }
! 305: }
! 306: unless ($disposition eq 'pending') {
! 307: my ($statusresult,$output) =
! 308: &Apache::loncoursequeueadmin::update_coursereq_status(\%requesthash,
! 309: $dom,$cnum,$reqstatus,'domain');
! 310: unless (&Apache::lonnet::del_dom($reqsnamespace,[$cnum.'_pending'],$dom) eq 'ok') {
! 311: print $fh &mt('An error occurred when removing the request for [_1] submitted by [_2] from the pending queue.',$instcode,$ownername.':'.$ownerdom);
! 312: }
! 313: }
! 314: }
! 315: }
! 316: }
! 317: foreach my $key (sort(keys(%newcids))) {
! 318: if (ref($newcids{$key}) eq 'ARRAY') {
! 319: print $fh "created course from queued request: $key - ".join(', ',@{$newcids{$key}})."\n";
! 320: my $newcourse = &LONCAPA::escape($key.':'.$newcids{$key});
! 321: }
! 322: }
! 323: return;
! 324: }
1.1 raeburn 325:
326: sub check_activedc {
327: my ($dcdom,$dcname,$defdom) = @_;
328: my %dumphash=
329: &Apache::lonnet::dump('roles',$dcdom,$dcname);
330: my $now=time;
331: my $activedc = 0;
332: foreach my $item (keys %dumphash) {
333: my ($domain,$role) = ($item =~ m-^/([^/]+)/[^_]*_(\w+)$-);
334: if ($role eq 'dc' && $domain eq $defdom) {
335: my ($trole,$tend,$tstart)=split(/_/,$dumphash{$item});
336: if (($tend) && ($tend<$now)) { next; }
337: if (($tstart) && ($now<$tstart)) { next; }
338: $activedc = 1;
339: last;
340: }
341: }
342: return $activedc;
343: }
1.3 raeburn 344:
345: sub set_permissions {
346: my ($permissionflags,$permissions) = @_;
347: foreach my $allowtype (@{$permissions}) {
1.4 raeburn 348: unless($env{"allowed.$allowtype"}) {
349: $env{"allowed.$allowtype"} = 'F';
1.12 ! raeburn 350: $permissionflags->{$allowtype} = 1;
1.3 raeburn 351: }
352: }
353: }
354:
355: sub unset_permissions {
356: my ($permissionflags) = @_;
1.12 ! raeburn 357: foreach my $allowtype (keys(%{$permissionflags})) {
1.4 raeburn 358: delete($env{"allowed.$allowtype"});
1.3 raeburn 359: }
360: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>